diff --git a/accessible/generic/DocAccessible.cpp b/accessible/generic/DocAccessible.cpp index 84902c5e8964..dd95d17848e5 100644 --- a/accessible/generic/DocAccessible.cpp +++ b/accessible/generic/DocAccessible.cpp @@ -1215,16 +1215,19 @@ Accessible* DocAccessible::GetAccessibleOrDescendant(nsINode* aNode) const { Accessible* acc = GetAccessible(aNode); if (acc) return acc; - acc = GetContainerAccessible(aNode); - if (acc == this && aNode == mContent) { - // The body node is the doc's content node. - return acc; + if (aNode == mContent || aNode == mDocumentNode->GetRootElement()) { + // If the node is the doc's body or root element, return the doc accessible. + return const_cast(this); } + acc = GetContainerAccessible(aNode); if (acc) { - uint32_t childCnt = acc->ChildCount(); + // We access the `mChildren` array directly so that we don't access + // lazily created children in places like `XULTreeAccessible` and + // `XULTreeGridAccessible`. + uint32_t childCnt = acc->mChildren.Length(); for (uint32_t idx = 0; idx < childCnt; idx++) { - Accessible* child = acc->GetChildAt(idx); + Accessible* child = acc->mChildren.ElementAt(idx); for (nsIContent* elm = child->GetContent(); elm && elm != acc->GetContent(); elm = elm->GetFlattenedTreeParent()) { diff --git a/browser/base/content/tabbrowser-tabs.js b/browser/base/content/tabbrowser-tabs.js index 3ee51bccaab3..1523316b0384 100644 --- a/browser/base/content/tabbrowser-tabs.js +++ b/browser/base/content/tabbrowser-tabs.js @@ -53,7 +53,7 @@ this._scrollButtonWidth = 0; this._lastNumPinned = 0; this._pinnedTabsLayoutCache = null; - this._animateElement = this.arrowScrollbox._scrollButtonDown; + this._animateElement = this.arrowScrollbox; this._tabClipWidth = Services.prefs.getIntPref( "browser.tabs.tabClipWidth" ); @@ -518,12 +518,11 @@ // return to avoid drawing the drop indicator var pixelsToScroll = 0; if (this.getAttribute("overflow") == "true") { - var targetAnonid = event.originalTarget.getAttribute("anonid"); - switch (targetAnonid) { - case "scrollbutton-up": + switch (event.originalTarget) { + case arrowScrollbox._scrollButtonUp: pixelsToScroll = arrowScrollbox.scrollIncrement * -1; break; - case "scrollbutton-down": + case arrowScrollbox._scrollButtonDown: pixelsToScroll = arrowScrollbox.scrollIncrement; break; } @@ -956,7 +955,7 @@ _initializeArrowScrollbox() { let arrowScrollbox = this.arrowScrollbox; - arrowScrollbox.addEventListener( + arrowScrollbox.shadowRoot.addEventListener( "underflow", event => { // Ignore underflow events: @@ -986,7 +985,7 @@ true ); - arrowScrollbox.addEventListener("overflow", event => { + arrowScrollbox.shadowRoot.addEventListener("overflow", event => { // Ignore overflow events: // - from nested scrollable elements // - for vertical orientation @@ -1002,7 +1001,7 @@ this._handleTabSelect(true); }); - // Override scrollbox.xml method, since our scrollbox's children are + // Override arrowscrollbox.js method, since our scrollbox's children are // inherited from the scrollbox binding parent (this). arrowScrollbox._getScrollableElements = () => { return this.allTabs.filter(arrowScrollbox._canScrollToElement); diff --git a/browser/components/aboutconfig/content/aboutconfig.css b/browser/components/aboutconfig/content/aboutconfig.css index 2bbcb3297d4a..391dee5b5e86 100644 --- a/browser/components/aboutconfig/content/aboutconfig.css +++ b/browser/components/aboutconfig/content/aboutconfig.css @@ -117,7 +117,7 @@ #prefs > tr.deleted > th { font-weight: bold; - opacity: 0.4; + color: var(--in-content-deemphasized-text); } #prefs > tr > td.cell-edit, @@ -182,3 +182,8 @@ td.cell-value > form > input[type="number"] { .button-reset:dir(rtl) { transform: scaleX(-1); } + +#prefs[has-visible-prefs] > .add > th, +#prefs[has-visible-prefs] > .add > td { + border-top: 1px solid var(--in-content-box-border-color); +} diff --git a/browser/components/aboutconfig/content/aboutconfig.js b/browser/components/aboutconfig/content/aboutconfig.js index a275d3e5e080..fe65eea69d34 100644 --- a/browser/components/aboutconfig/content/aboutconfig.js +++ b/browser/components/aboutconfig/content/aboutconfig.js @@ -71,12 +71,13 @@ let gFilterPattern = null; let gFilterShowAll = false; class PrefRow { - constructor(name) { + constructor(name, opts) { this.name = name; this.value = true; this.hidden = false; this.odd = false; this.editing = false; + this.isAddRow = opts && opts.isAddRow; this.refreshValue(); } @@ -336,6 +337,7 @@ class PrefRow { (this.hasUserValue ? "has-user-value " : "") + (this.isLocked ? "locked " : "") + (this.exists ? "" : "deleted ") + + (this.isAddRow ? "add " : "") + (this.odd ? "odd " : ""); } @@ -516,8 +518,11 @@ function loadPrefs() { let button = event.target.closest("button"); if (button.classList.contains("button-add")) { + pref.isAddRow = false; Preferences.set(pref.name, pref.value); - if (pref.type != "Boolean") { + if (pref.type == "Boolean") { + pref.refreshClass(); + } else { pref.edit(); } } else if ( @@ -583,6 +588,7 @@ function filterPrefs(options = {}) { let indexInArray = 0; let elementInTable = gPrefsTable.firstElementChild; let odd = false; + let hasVisiblePrefs = false; while (indexInArray < prefArray.length || elementInTable) { // For efficiency, filter the array while we are iterating. let prefInArray = prefArray[indexInArray]; @@ -630,14 +636,17 @@ function filterPrefs(options = {}) { prefInArray.refreshClass(); odd = !odd; indexInArray++; + hasVisiblePrefs = true; } if (fragment) { gPrefsTable.appendChild(fragment); } + gPrefsTable.toggleAttribute("has-visible-prefs", hasVisiblePrefs); + if (searchName && !gExistingPrefs.has(searchName)) { - let addPrefRow = new PrefRow(searchName); + let addPrefRow = new PrefRow(searchName, { isAddRow: true }); addPrefRow.odd = odd; gPrefsTable.appendChild(addPrefRow.getElement()); } diff --git a/browser/components/aboutconfig/test/browser/browser_edit.js b/browser/components/aboutconfig/test/browser/browser_edit.js index 855d0f96633e..524ebc6b6818 100644 --- a/browser/components/aboutconfig/test/browser/browser_edit.js +++ b/browser/components/aboutconfig/test/browser/browser_edit.js @@ -34,19 +34,22 @@ add_task(async function test_add_user_pref() { await AboutConfigTest.withNewTab(async function() { // The row for a new preference appears when searching for its name. Assert.ok(!this.getRow(PREF_NEW)); - this.search(PREF_NEW); - let row = this.getRow(PREF_NEW); - Assert.ok(row.hasClass("deleted")); for (let [radioIndex, expectedValue, expectedEditingMode] of [ [0, true, false], [1, 0, true], [2, "", true], ]) { + this.search(PREF_NEW); + let row = this.getRow(PREF_NEW); + Assert.ok(row.hasClass("deleted")); + Assert.ok(row.hasClass("add")); + // Adding the preference should set the default for the data type. row.element.querySelectorAll("input")[radioIndex].click(); row.editColumnButton.click(); Assert.ok(!row.hasClass("deleted")); + Assert.ok(!row.hasClass("add")); Assert.ok(Preferences.get(PREF_NEW) === expectedValue); // Number and String preferences should be in edit mode. @@ -56,6 +59,7 @@ add_task(async function test_add_user_pref() { this.search(PREF_NEW); row = this.getRow(PREF_NEW); Assert.ok(!row.hasClass("deleted")); + Assert.ok(!row.hasClass("add")); Assert.ok(!row.valueInput); // Reset the preference, then continue by adding a different type. diff --git a/browser/components/aboutconfig/test/browser/browser_search.js b/browser/components/aboutconfig/test/browser/browser_search.js index d5d736287829..5bf1c2bf46dd 100644 --- a/browser/components/aboutconfig/test/browser/browser_search.js +++ b/browser/components/aboutconfig/test/browser/browser_search.js @@ -54,6 +54,10 @@ add_task(async function test_search() { // Expecting 1 row to be returned since it offers the ability to add. this.search("aJunkValueasdf"); Assert.equal(this.rows.length, 1); + // The has-visible-prefs attribute is used to style the border of the add row. + Assert.ok(!this.prefsTable.hasAttribute("has-visible-prefs")); + let addRow = this.getRow("aJunkValueasdf"); + Assert.equal(getComputedStyle(addRow.valueCell)["border-top-width"], "0px"); // Pressing ESC clears the field and returns to the initial page. EventUtils.sendKey("escape"); @@ -68,10 +72,15 @@ add_task(async function test_search() { // new preference with the same name but a different case. this.search("TEST.aboutconfig.a"); Assert.equal(this.rows.length, 3); + // The has-visible-prefs attribute is used to style the border of the add row. + Assert.ok(this.prefsTable.hasAttribute("has-visible-prefs")); + addRow = this.getRow("TEST.aboutconfig.a"); + Assert.equal(getComputedStyle(addRow.valueCell)["border-top-width"], "1px"); // Entering an empty string returns to the initial page. this.search(""); Assert.equal(this.rows.length, 0); + Assert.ok(!this.prefsTable.hasAttribute("has-visible-prefs")); }); }); diff --git a/browser/components/places/content/places-menupopup.js b/browser/components/places/content/places-menupopup.js index 853acd8d27bd..fbb256e0c3a0 100644 --- a/browser/components/places/content/places-menupopup.js +++ b/browser/components/places/content/places-menupopup.js @@ -479,11 +479,10 @@ } // Autoscroll the popup strip if we drag over the scroll buttons. - let anonid = event.originalTarget.getAttribute("anonid"); let scrollDir = 0; - if (anonid == "scrollbutton-up") { + if (event.originalTarget == this.scrollBox._scrollButtonUp) { scrollDir = -1; - } else if (anonid == "scrollbutton-down") { + } else if (event.originalTarget == this.scrollBox._scrollButtonDown) { scrollDir = 1; } if (scrollDir != 0) { diff --git a/browser/components/preferences/in-content/syncChooseWhatToSync.xul b/browser/components/preferences/in-content/syncChooseWhatToSync.xul index 7a1cd76f4960..4c20c2b050bc 100644 --- a/browser/components/preferences/in-content/syncChooseWhatToSync.xul +++ b/browser/components/preferences/in-content/syncChooseWhatToSync.xul @@ -24,7 +24,6 @@ \n\n\n\n\n\n// WEBPACK FOOTER //\n// src/App.vue","\n\n\n\n\n\n\n\n// WEBPACK FOOTER //\n// src/components/NavBar.vue","\n\n\n\n\n\n\n\n// WEBPACK FOOTER //\n// src/components/NavMenu.vue","\n\n\n\n\n\n\n\n// WEBPACK FOOTER //\n// src/components/OptionsPage.vue","\n\n\n\n\n\n\n\n// WEBPACK FOOTER //\n// src/components/PassViewPage.vue","\n\n\n\n\n\n\n\n// WEBPACK FOOTER //\n// src/components/RenderTaskViewPage.vue","\n\n\n\n\n\n\n\n// WEBPACK FOOTER //\n// src/components/TreeView.vue","\n\n\n\n\n\n\n\n// WEBPACK FOOTER //\n// src/components/DocumentViewPage.vue","\n\n\n\n\n\n\n\n// WEBPACK FOOTER //\n// src/components/ClipScrollTreeViewPage.vue","\n\n\n\n\n\n\n\n// WEBPACK FOOTER //\n// src/components/ScreenshotPage.vue","import Vue from 'vue';\nimport Buefy from 'buefy';\nimport 'buefy/dist/buefy.css';\nimport \"vue-material-design-icons/styles.css\";\nimport App from './App.vue';\nimport store from './store';\n\nVue.use(Buefy);\n\nnew Vue({\n el: '#app',\n store,\n render: h => h(App)\n})\n\n\n\n// WEBPACK FOOTER //\n// ./src/main.js","var scope = (typeof global !== \"undefined\" && global) ||\n (typeof self !== \"undefined\" && self) ||\n window;\nvar apply = Function.prototype.apply;\n\n// DOM APIs, for completeness\n\nexports.setTimeout = function() {\n return new Timeout(apply.call(setTimeout, scope, arguments), clearTimeout);\n};\nexports.setInterval = function() {\n return new Timeout(apply.call(setInterval, scope, arguments), clearInterval);\n};\nexports.clearTimeout =\nexports.clearInterval = function(timeout) {\n if (timeout) {\n timeout.close();\n }\n};\n\nfunction Timeout(id, clearFn) {\n this._id = id;\n this._clearFn = clearFn;\n}\nTimeout.prototype.unref = Timeout.prototype.ref = function() {};\nTimeout.prototype.close = function() {\n this._clearFn.call(scope, this._id);\n};\n\n// Does not start the time, just sets up the members needed.\nexports.enroll = function(item, msecs) {\n clearTimeout(item._idleTimeoutId);\n item._idleTimeout = msecs;\n};\n\nexports.unenroll = function(item) {\n clearTimeout(item._idleTimeoutId);\n item._idleTimeout = -1;\n};\n\nexports._unrefActive = exports.active = function(item) {\n clearTimeout(item._idleTimeoutId);\n\n var msecs = item._idleTimeout;\n if (msecs >= 0) {\n item._idleTimeoutId = setTimeout(function onTimeout() {\n if (item._onTimeout)\n item._onTimeout();\n }, msecs);\n }\n};\n\n// setimmediate attaches itself to the global object\nrequire(\"setimmediate\");\n// On some exotic environments, it's not clear which object `setimmediate` was\n// able to install onto. Search each possibility in the same order as the\n// `setimmediate` library.\nexports.setImmediate = (typeof self !== \"undefined\" && self.setImmediate) ||\n (typeof global !== \"undefined\" && global.setImmediate) ||\n (this && this.setImmediate);\nexports.clearImmediate = (typeof self !== \"undefined\" && self.clearImmediate) ||\n (typeof global !== \"undefined\" && global.clearImmediate) ||\n (this && this.clearImmediate);\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/timers-browserify/main.js\n// module id = 17\n// module chunks = 0","(function (global, undefined) {\n \"use strict\";\n\n if (global.setImmediate) {\n return;\n }\n\n var nextHandle = 1; // Spec says greater than zero\n var tasksByHandle = {};\n var currentlyRunningATask = false;\n var doc = global.document;\n var registerImmediate;\n\n function setImmediate(callback) {\n // Callback can either be a function or a string\n if (typeof callback !== \"function\") {\n callback = new Function(\"\" + callback);\n }\n // Copy function arguments\n var args = new Array(arguments.length - 1);\n for (var i = 0; i < args.length; i++) {\n args[i] = arguments[i + 1];\n }\n // Store and register the task\n var task = { callback: callback, args: args };\n tasksByHandle[nextHandle] = task;\n registerImmediate(nextHandle);\n return nextHandle++;\n }\n\n function clearImmediate(handle) {\n delete tasksByHandle[handle];\n }\n\n function run(task) {\n var callback = task.callback;\n var args = task.args;\n switch (args.length) {\n case 0:\n callback();\n break;\n case 1:\n callback(args[0]);\n break;\n case 2:\n callback(args[0], args[1]);\n break;\n case 3:\n callback(args[0], args[1], args[2]);\n break;\n default:\n callback.apply(undefined, args);\n break;\n }\n }\n\n function runIfPresent(handle) {\n // From the spec: \"Wait until any invocations of this algorithm started before this one have completed.\"\n // So if we're currently running a task, we'll need to delay this invocation.\n if (currentlyRunningATask) {\n // Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a\n // \"too much recursion\" error.\n setTimeout(runIfPresent, 0, handle);\n } else {\n var task = tasksByHandle[handle];\n if (task) {\n currentlyRunningATask = true;\n try {\n run(task);\n } finally {\n clearImmediate(handle);\n currentlyRunningATask = false;\n }\n }\n }\n }\n\n function installNextTickImplementation() {\n registerImmediate = function(handle) {\n process.nextTick(function () { runIfPresent(handle); });\n };\n }\n\n function canUsePostMessage() {\n // The test against `importScripts` prevents this implementation from being installed inside a web worker,\n // where `global.postMessage` means something completely different and can't be used for this purpose.\n if (global.postMessage && !global.importScripts) {\n var postMessageIsAsynchronous = true;\n var oldOnMessage = global.onmessage;\n global.onmessage = function() {\n postMessageIsAsynchronous = false;\n };\n global.postMessage(\"\", \"*\");\n global.onmessage = oldOnMessage;\n return postMessageIsAsynchronous;\n }\n }\n\n function installPostMessageImplementation() {\n // Installs an event handler on `global` for the `message` event: see\n // * https://developer.mozilla.org/en/DOM/window.postMessage\n // * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages\n\n var messagePrefix = \"setImmediate$\" + Math.random() + \"$\";\n var onGlobalMessage = function(event) {\n if (event.source === global &&\n typeof event.data === \"string\" &&\n event.data.indexOf(messagePrefix) === 0) {\n runIfPresent(+event.data.slice(messagePrefix.length));\n }\n };\n\n if (global.addEventListener) {\n global.addEventListener(\"message\", onGlobalMessage, false);\n } else {\n global.attachEvent(\"onmessage\", onGlobalMessage);\n }\n\n registerImmediate = function(handle) {\n global.postMessage(messagePrefix + handle, \"*\");\n };\n }\n\n function installMessageChannelImplementation() {\n var channel = new MessageChannel();\n channel.port1.onmessage = function(event) {\n var handle = event.data;\n runIfPresent(handle);\n };\n\n registerImmediate = function(handle) {\n channel.port2.postMessage(handle);\n };\n }\n\n function installReadyStateChangeImplementation() {\n var html = doc.documentElement;\n registerImmediate = function(handle) {\n // Create a \n\n\n\n\n\n// WEBPACK FOOTER //\n// src/App.vue","\n\n\n\n\n\n\n\n// WEBPACK FOOTER //\n// src/components/NavBar.vue","\n\n\n\n\n\n\n\n// WEBPACK FOOTER //\n// src/components/NavMenu.vue","\n\n\n\n\n\n\n\n// WEBPACK FOOTER //\n// src/components/OptionsPage.vue","\n\n\n\n\n\n\n\n// WEBPACK FOOTER //\n// src/components/PassViewPage.vue","\n\n\n\n\n\n\n\n// WEBPACK FOOTER //\n// src/components/RenderTaskViewPage.vue","\n\n\n\n\n\n\n\n// WEBPACK FOOTER //\n// src/components/TreeView.vue","\n\n\n\n\n\n\n\n// WEBPACK FOOTER //\n// src/components/DocumentViewPage.vue","\n\n\n\n\n\n\n\n// WEBPACK FOOTER //\n// src/components/ClipScrollTreeViewPage.vue","\n\n\n\n\n\n\n\n// WEBPACK FOOTER //\n// src/components/ScreenshotPage.vue","import Vue from 'vue';\nimport Buefy from 'buefy';\nimport 'buefy/dist/buefy.css';\nimport \"vue-material-design-icons/styles.css\";\nimport App from './App.vue';\nimport store from './store';\n\nVue.use(Buefy);\n\nnew Vue({\n el: '#app',\n store,\n render: h => h(App)\n})\n\n\n\n// WEBPACK FOOTER //\n// ./src/main.js","var scope = (typeof global !== \"undefined\" && global) ||\n (typeof self !== \"undefined\" && self) ||\n window;\nvar apply = Function.prototype.apply;\n\n// DOM APIs, for completeness\n\nexports.setTimeout = function() {\n return new Timeout(apply.call(setTimeout, scope, arguments), clearTimeout);\n};\nexports.setInterval = function() {\n return new Timeout(apply.call(setInterval, scope, arguments), clearInterval);\n};\nexports.clearTimeout =\nexports.clearInterval = function(timeout) {\n if (timeout) {\n timeout.close();\n }\n};\n\nfunction Timeout(id, clearFn) {\n this._id = id;\n this._clearFn = clearFn;\n}\nTimeout.prototype.unref = Timeout.prototype.ref = function() {};\nTimeout.prototype.close = function() {\n this._clearFn.call(scope, this._id);\n};\n\n// Does not start the time, just sets up the members needed.\nexports.enroll = function(item, msecs) {\n clearTimeout(item._idleTimeoutId);\n item._idleTimeout = msecs;\n};\n\nexports.unenroll = function(item) {\n clearTimeout(item._idleTimeoutId);\n item._idleTimeout = -1;\n};\n\nexports._unrefActive = exports.active = function(item) {\n clearTimeout(item._idleTimeoutId);\n\n var msecs = item._idleTimeout;\n if (msecs >= 0) {\n item._idleTimeoutId = setTimeout(function onTimeout() {\n if (item._onTimeout)\n item._onTimeout();\n }, msecs);\n }\n};\n\n// setimmediate attaches itself to the global object\nrequire(\"setimmediate\");\n// On some exotic environments, it's not clear which object `setimmediate` was\n// able to install onto. Search each possibility in the same order as the\n// `setimmediate` library.\nexports.setImmediate = (typeof self !== \"undefined\" && self.setImmediate) ||\n (typeof global !== \"undefined\" && global.setImmediate) ||\n (this && this.setImmediate);\nexports.clearImmediate = (typeof self !== \"undefined\" && self.clearImmediate) ||\n (typeof global !== \"undefined\" && global.clearImmediate) ||\n (this && this.clearImmediate);\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/timers-browserify/main.js\n// module id = 17\n// module chunks = 0","(function (global, undefined) {\n \"use strict\";\n\n if (global.setImmediate) {\n return;\n }\n\n var nextHandle = 1; // Spec says greater than zero\n var tasksByHandle = {};\n var currentlyRunningATask = false;\n var doc = global.document;\n var registerImmediate;\n\n function setImmediate(callback) {\n // Callback can either be a function or a string\n if (typeof callback !== \"function\") {\n callback = new Function(\"\" + callback);\n }\n // Copy function arguments\n var args = new Array(arguments.length - 1);\n for (var i = 0; i < args.length; i++) {\n args[i] = arguments[i + 1];\n }\n // Store and register the task\n var task = { callback: callback, args: args };\n tasksByHandle[nextHandle] = task;\n registerImmediate(nextHandle);\n return nextHandle++;\n }\n\n function clearImmediate(handle) {\n delete tasksByHandle[handle];\n }\n\n function run(task) {\n var callback = task.callback;\n var args = task.args;\n switch (args.length) {\n case 0:\n callback();\n break;\n case 1:\n callback(args[0]);\n break;\n case 2:\n callback(args[0], args[1]);\n break;\n case 3:\n callback(args[0], args[1], args[2]);\n break;\n default:\n callback.apply(undefined, args);\n break;\n }\n }\n\n function runIfPresent(handle) {\n // From the spec: \"Wait until any invocations of this algorithm started before this one have completed.\"\n // So if we're currently running a task, we'll need to delay this invocation.\n if (currentlyRunningATask) {\n // Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a\n // \"too much recursion\" error.\n setTimeout(runIfPresent, 0, handle);\n } else {\n var task = tasksByHandle[handle];\n if (task) {\n currentlyRunningATask = true;\n try {\n run(task);\n } finally {\n clearImmediate(handle);\n currentlyRunningATask = false;\n }\n }\n }\n }\n\n function installNextTickImplementation() {\n registerImmediate = function(handle) {\n process.nextTick(function () { runIfPresent(handle); });\n };\n }\n\n function canUsePostMessage() {\n // The test against `importScripts` prevents this implementation from being installed inside a web worker,\n // where `global.postMessage` means something completely different and can't be used for this purpose.\n if (global.postMessage && !global.importScripts) {\n var postMessageIsAsynchronous = true;\n var oldOnMessage = global.onmessage;\n global.onmessage = function() {\n postMessageIsAsynchronous = false;\n };\n global.postMessage(\"\", \"*\");\n global.onmessage = oldOnMessage;\n return postMessageIsAsynchronous;\n }\n }\n\n function installPostMessageImplementation() {\n // Installs an event handler on `global` for the `message` event: see\n // * https://developer.mozilla.org/en/DOM/window.postMessage\n // * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages\n\n var messagePrefix = \"setImmediate$\" + Math.random() + \"$\";\n var onGlobalMessage = function(event) {\n if (event.source === global &&\n typeof event.data === \"string\" &&\n event.data.indexOf(messagePrefix) === 0) {\n runIfPresent(+event.data.slice(messagePrefix.length));\n }\n };\n\n if (global.addEventListener) {\n global.addEventListener(\"message\", onGlobalMessage, false);\n } else {\n global.attachEvent(\"onmessage\", onGlobalMessage);\n }\n\n registerImmediate = function(handle) {\n global.postMessage(messagePrefix + handle, \"*\");\n };\n }\n\n function installMessageChannelImplementation() {\n var channel = new MessageChannel();\n channel.port1.onmessage = function(event) {\n var handle = event.data;\n runIfPresent(handle);\n };\n\n registerImmediate = function(handle) {\n channel.port2.postMessage(handle);\n };\n }\n\n function installReadyStateChangeImplementation() {\n var html = doc.documentElement;\n registerImmediate = function(handle) {\n // Create a