diff --git a/b2g/components/PaymentGlue.js b/b2g/components/PaymentGlue.js index 18ae85e9aff7..3ca8d82c20d6 100644 --- a/b2g/components/PaymentGlue.js +++ b/b2g/components/PaymentGlue.js @@ -66,7 +66,7 @@ PaymentUI.prototype = { // Once the user confirm the payment request and makes his choice, we get // back to the DOM part to get the appropriate payment flow information // based on the selected payment provider. - content.addEventListener("mozContentEvent", function handleSelection(evt) { + this._handleSelection = (function _handleSelection(evt) { let msg = evt.detail; if (msg.id != id) { return; @@ -78,8 +78,10 @@ PaymentUI.prototype = { _error(msg.errorMsg); } - content.removeEventListener("mozContentEvent", handleSelection); - }); + content.removeEventListener("mozContentEvent", this._handleSelection); + this._handleSelection = null; + }).bind(this); + content.addEventListener("mozContentEvent", this._handleSelection); browser.shell.sendChromeEvent(detail); }, @@ -114,21 +116,31 @@ PaymentUI.prototype = { // At some point the UI would send the created iframe back so the // callbacks for firing DOMRequest events can be loaded on its // content. - content.addEventListener("mozContentEvent", (function loadPaymentShim(evt) { - if (evt.detail.id != id) { - content.removeEventListener("mozContentEvent", loadPaymentShim); + this._loadPaymentShim = (function _loadPaymentShim(evt) { + let msg = evt.detail; + if (msg.id != id) { + return; + } + + if (msg.errorMsg) { + content.removeEventListener("mozContentEvent", this._loadPaymentShim); + this._loadPaymentShim = null; + _error("ERROR_LOADING_PAYMENT_SHIM: " + msg.errorMsg); + return; + } + + if (!msg.frame) { + content.removeEventListener("mozContentEvent", this._loadPaymentShim); + this._loadPaymentShim = null; + _error("ERROR_LOADING_PAYMENT_SHIM"); return; } // Try to load the payment shim file containing the payment callbacks // in the content script. - if (!evt.detail.frame && !evt.detail.errorMsg) { - _error("ERROR_LOADING_PAYMENT_SHIM"); - return; - } - let frame = evt.detail.frame; + let frame = msg.frame; let frameLoader = frame.QueryInterface(Ci.nsIFrameLoaderOwner) - .frameLoader; + .frameLoader; let mm = frameLoader.messageManager; try { mm.loadFrameScript(kPaymentShimFile, true); @@ -140,25 +152,33 @@ PaymentUI.prototype = { } _error("ERROR_LOADING_PAYMENT_SHIM"); } finally { - content.removeEventListener("mozContentEvent", loadPaymentShim); + content.removeEventListener("mozContentEvent", this._loadPaymentShim); + this._loadPaymentShim = null; } - }).bind(this)); + }).bind(this); + content.addEventListener("mozContentEvent", this._loadPaymentShim); // We also listen for UI notifications about a closed payment flow. The UI // should provide the reason of the closure within the 'errorMsg' parameter - this._notifyPayFlowClosed = function _notifyPayFlowClosed (evt) { - if (evt.detail.id != id) { + this._notifyPayFlowClosed = (function _notifyPayFlowClosed(evt) { + let msg = evt.detail; + if (msg.id != id) { return; } - if (evt.detail.errorMsg) { - _error(evt.detail.errorMsg); - content.removeEventListener("mozContentEvent", - this._notifyPayFlowClosed); + + if (msg.type != 'cancel') { return; } - }; + + if (msg.errorMsg) { + _error(msg.errorMsg); + } + content.removeEventListener("mozContentEvent", + this._notifyPayFlowClosed); + this._notifyPayFlowClosed = null; + }).bind(this); content.addEventListener("mozContentEvent", - this._notifyPayFlowClosed.bind(this)); + this._notifyPayFlowClosed); browser.shell.sendChromeEvent(detail); }, @@ -169,7 +189,21 @@ PaymentUI.prototype = { if (!content) { return; } - content.removeEventListener("mozContentEvent", this._notifyPayFlowClosed); + + if (this._handleSelection) { + content.removeEventListener("mozContentEvent", this._handleSelection); + this._handleSelection = null; + } + + if (this._notifyPayFlowClosed) { + content.removeEventListener("mozContentEvent", this._notifyPayFlowClosed); + this._notifyPayFlowClosed = null; + } + + if (this._loadPaymentShim) { + content.removeEventListener("mozContentEvent", this._loadPaymentShim); + this._loadPaymentShim = null; + } }, getRandomId: function getRandomId() { diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index b6c7503616bc..0d831f49cdf2 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,4 @@ { - "revision": "fefcf4aab784c6e9a0c8b3f4b282c21fb0e0ac6e", + "revision": "70f416f866858cb2068bffa31118fc4b15b482c9", "repo_path": "/integration/gaia-central" } diff --git a/browser/components/sessionstore/src/SessionWorker.js b/browser/components/sessionstore/src/SessionWorker.js index c62464265eed..276d8d7aa51e 100644 --- a/browser/components/sessionstore/src/SessionWorker.js +++ b/browser/components/sessionstore/src/SessionWorker.js @@ -87,7 +87,8 @@ let Agent = { return { result: this.initialState, - telemetry: {FX_SESSION_RESTORE_READ_FILE_MS: durationMs} + telemetry: {FX_SESSION_RESTORE_READ_FILE_MS: durationMs, + FX_SESSION_RESTORE_FILE_SIZE_BYTES: bytes.byteLength} }; } catch (ex if isNoSuchFileEx(ex)) { // Ignore exceptions about non-existent files. diff --git a/browser/components/sessionstore/src/TabState.jsm b/browser/components/sessionstore/src/TabState.jsm index f1448db3616f..3cf7b3bcea8c 100644 --- a/browser/components/sessionstore/src/TabState.jsm +++ b/browser/components/sessionstore/src/TabState.jsm @@ -168,6 +168,11 @@ let TabStateInternal = { // text and scroll data. let history = yield Messenger.send(tab, "SessionStore:collectSessionHistory"); + // The tab could have been closed while waiting for a response. + if (!tab.linkedBrowser) { + return; + } + // Collect basic tab data, without session history and storage. let tabData = this._collectBaseTabData(tab); diff --git a/browser/components/sessionstore/test/content.js b/browser/components/sessionstore/test/content.js index e25a3e792b01..e839d1375651 100644 --- a/browser/components/sessionstore/test/content.js +++ b/browser/components/sessionstore/test/content.js @@ -29,13 +29,13 @@ addMessageListener("ss-test:getStyleSheets", function (msg) { addMessageListener("ss-test:enableStyleSheetsForSet", function (msg) { content.document.enableStyleSheetsForSet(msg.data); - sendSyncMessage("ss-test:enableStyleSheetsForSet"); + sendAsyncMessage("ss-test:enableStyleSheetsForSet"); }); addMessageListener("ss-test:enableSubDocumentStyleSheetsForSet", function (msg) { let iframe = content.document.getElementById(msg.data.id); iframe.contentDocument.enableStyleSheetsForSet(msg.data.set); - sendSyncMessage("ss-test:enableSubDocumentStyleSheetsForSet"); + sendAsyncMessage("ss-test:enableSubDocumentStyleSheetsForSet"); }); addMessageListener("ss-test:getAuthorStyleDisabled", function (msg) { diff --git a/browser/devtools/markupview/test/browser_inspector_markup_765105_tooltip.js b/browser/devtools/markupview/test/browser_inspector_markup_765105_tooltip.js index 2764651aa86b..a3d3dfc50817 100644 --- a/browser/devtools/markupview/test/browser_inspector_markup_765105_tooltip.js +++ b/browser/devtools/markupview/test/browser_inspector_markup_765105_tooltip.js @@ -91,9 +91,6 @@ function testImageTooltip(index) { assertTooltipShownOn(target, () => { let images = markup.tooltip.panel.getElementsByTagName("image"); is(images.length, 1, "Tooltip for [" + TEST_NODES[index] + "] contains an image"); - if (isImg) { - compareImageData(node, images[0].src); - } markup.tooltip.hide(); diff --git a/browser/devtools/netmonitor/netmonitor-view.js b/browser/devtools/netmonitor/netmonitor-view.js index a6ce58834a4a..6cca63cfe007 100644 --- a/browser/devtools/netmonitor/netmonitor-view.js +++ b/browser/devtools/netmonitor/netmonitor-view.js @@ -557,6 +557,17 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, { this.refreshZebra(); }, + /** + * Removes all network requests and closes the sidebar if open. + */ + clear: function() { + NetMonitorView.Sidebar.toggle(false); + $("#details-pane-toggle").disabled = true; + + this.empty(); + this.refreshSummary(); + }, + /** * Predicates used when filtering items. * diff --git a/browser/devtools/netmonitor/netmonitor.xul b/browser/devtools/netmonitor/netmonitor.xul index e984a6c98670..6f508272a6fe 100644 --- a/browser/devtools/netmonitor/netmonitor.xul +++ b/browser/devtools/netmonitor/netmonitor.xul @@ -210,6 +210,11 @@ class="plain requests-menu-footer-label" flex="1" crop="end"/> + diff --git a/browser/devtools/netmonitor/test/browser.ini b/browser/devtools/netmonitor/test/browser.ini index 2b51210b3a11..4bb0b9e47369 100644 --- a/browser/devtools/netmonitor/test/browser.ini +++ b/browser/devtools/netmonitor/test/browser.ini @@ -26,6 +26,7 @@ support-files = [browser_net_accessibility-01.js] [browser_net_accessibility-02.js] [browser_net_autoscroll.js] +[browser_net_clear.js] [browser_net_content-type.js] [browser_net_copy_url.js] [browser_net_cyrillic-01.js] diff --git a/browser/devtools/netmonitor/test/browser_net_clear.js b/browser/devtools/netmonitor/test/browser_net_clear.js new file mode 100644 index 000000000000..a78492cd7b87 --- /dev/null +++ b/browser/devtools/netmonitor/test/browser_net_clear.js @@ -0,0 +1,76 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if the clear button empties the request menu. + */ + +function test() { + initNetMonitor(SIMPLE_URL).then(([aTab, aDebuggee, aMonitor]) => { + info("Starting test... "); + + let { document, $, NetMonitorView } = aMonitor.panelWin; + let { RequestsMenu } = NetMonitorView; + let detailsPane = $("#details-pane"); + let detailsPaneToggleButton = $('#details-pane-toggle'); + let clearButton = $('#requests-menu-clear-button'); + + RequestsMenu.lazyUpdate = false; + + // Make sure we start in a sane state + assertNoRequestState(RequestsMenu, detailsPaneToggleButton); + + // Load one request and assert it shows up in the lis + aMonitor.panelWin.once(aMonitor.panelWin.EVENTS.NETWORK_EVENT, () => { + assertSingleRequestState(RequestsMenu, detailsPaneToggleButton); + + // Click clear and make sure the requests are gone + EventUtils.sendMouseEvent({ type: "click" }, clearButton); + assertNoRequestState(RequestsMenu, detailsPaneToggleButton); + + // Load a second request and make sure they still show up + aMonitor.panelWin.once(aMonitor.panelWin.EVENTS.NETWORK_EVENT, () => { + assertSingleRequestState(RequestsMenu, detailsPaneToggleButton); + + // Make sure we can now open the details pane + NetMonitorView.toggleDetailsPane({ visible: true, animated: false }); + ok(!detailsPane.hasAttribute("pane-collapsed") && + !detailsPaneToggleButton.hasAttribute("pane-collapsed"), + "The details pane should be visible after clicking the toggle button."); + + // Click clear and make sure the details pane closes + EventUtils.sendMouseEvent({ type: "click" }, clearButton); + assertNoRequestState(RequestsMenu, detailsPaneToggleButton); + ok(detailsPane.hasAttribute("pane-collapsed") && + detailsPaneToggleButton.hasAttribute("pane-collapsed"), + "The details pane should not be visible clicking 'clear'."); + + teardown(aMonitor).then(finish); + }); + + aDebuggee.location.reload(); + }); + + aDebuggee.location.reload(); + }); + + /** + * Asserts the state of the network monitor when one request has loaded + */ + function assertSingleRequestState(RequestsMenu, detailsPaneToggleButton) { + is(RequestsMenu.itemCount, 1, + "The request menu should have one item at this point."); + is(detailsPaneToggleButton.hasAttribute("disabled"), false, + "The pane toggle button should be enabled after a request is made."); + } + + /** + * Asserts the state of the network monitor when no requests have loaded + */ + function assertNoRequestState(RequestsMenu, detailsPaneToggleButton) { + is(RequestsMenu.itemCount, 0, + "The request menu should be empty at this point."); + is(detailsPaneToggleButton.hasAttribute("disabled"), true, + "The pane toggle button should be disabled when the request menu is cleared."); + } +} diff --git a/browser/devtools/scratchpad/test/browser_scratchpad_ui.js b/browser/devtools/scratchpad/test/browser_scratchpad_ui.js index eac788a4cab5..9d3e8a84d273 100644 --- a/browser/devtools/scratchpad/test/browser_scratchpad_ui.js +++ b/browser/devtools/scratchpad/test/browser_scratchpad_ui.js @@ -35,9 +35,6 @@ function runTests() }; let lastMethodCalled = null; - sp.__noSuchMethod__ = function(aMethodName) { - lastMethodCalled = aMethodName; - }; for (let id in methodsAndItems) { lastMethodCalled = null; @@ -46,7 +43,9 @@ function runTests() let oldMethod = sp[methodName]; ok(oldMethod, "found method " + methodName + " in Scratchpad object"); - delete sp[methodName]; + sp[methodName] = () => { + lastMethodCalled = methodName; + } let menu = doc.getElementById(id); ok(menu, "found menuitem #" + id); @@ -64,7 +63,5 @@ function runTests() sp[methodName] = oldMethod; } - delete sp.__noSuchMethod__; - finish(); } diff --git a/browser/devtools/shadereditor/shadereditor.js b/browser/devtools/shadereditor/shadereditor.js index 615e4fd88233..b887c0946fca 100644 --- a/browser/devtools/shadereditor/shadereditor.js +++ b/browser/devtools/shadereditor/shadereditor.js @@ -32,7 +32,7 @@ const EVENTS = { }; const STRINGS_URI = "chrome://browser/locale/devtools/shadereditor.properties" -const HIGHLIGHT_COLOR = [1, 0, 0, 1]; // rgba +const HIGHLIGHT_TINT = [1, 0, 0.25, 1]; // rgba const TYPING_MAX_DELAY = 500; // ms const SHADERS_AUTOGROW_ITEMS = 4; const GUTTER_ERROR_PANEL_OFFSET_X = 7; // px @@ -297,7 +297,7 @@ let ShadersListView = Heritage.extend(WidgetMethods, { _onProgramMouseEnter: function(e) { let sourceItem = this.getItemForElement(e.target, { noSiblings: true }); if (sourceItem && !sourceItem.attachment.isBlackBoxed) { - sourceItem.attachment.programActor.highlight(HIGHLIGHT_COLOR); + sourceItem.attachment.programActor.highlight(HIGHLIGHT_TINT); if (e instanceof Event) { e.preventDefault(); diff --git a/browser/devtools/shadereditor/test/browser.ini b/browser/devtools/shadereditor/test/browser.ini index 7efa1dd6587e..c90e31cd656e 100644 --- a/browser/devtools/shadereditor/test/browser.ini +++ b/browser/devtools/shadereditor/test/browser.ini @@ -1,5 +1,6 @@ [DEFAULT] support-files = + doc_blended-geometry.html doc_multiple-contexts.html doc_overlapping-geometry.html doc_shader-order.html @@ -14,9 +15,11 @@ support-files = [browser_se_editors-lazy-init.js] [browser_se_first-run.js] [browser_se_navigation.js] -[browser_se_programs-blackbox.js] +[browser_se_programs-blackbox-01.js] +[browser_se_programs-blackbox-02.js] [browser_se_programs-cache.js] -[browser_se_programs-highlight.js] +[browser_se_programs-highlight-01.js] +[browser_se_programs-highlight-02.js] [browser_se_programs-list.js] [browser_se_shaders-edit-01.js] [browser_se_shaders-edit-02.js] diff --git a/browser/devtools/shadereditor/test/browser_se_programs-blackbox.js b/browser/devtools/shadereditor/test/browser_se_programs-blackbox-01.js similarity index 95% rename from browser/devtools/shadereditor/test/browser_se_programs-blackbox.js rename to browser/devtools/shadereditor/test/browser_se_programs-blackbox-01.js index e39f8a543943..0e6a3e693af2 100644 --- a/browser/devtools/shadereditor/test/browser_se_programs-blackbox.js +++ b/browser/devtools/shadereditor/test/browser_se_programs-blackbox-01.js @@ -84,7 +84,7 @@ function ifWebGLSupported() { yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2"); yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1"); yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2"); - ok(true, "Highlighting didn't work while blackboxed (1)."); + ok(true, "Highlighting shouldn't work while blackboxed (1)."); ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 0) }); ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 1) }); @@ -93,7 +93,7 @@ function ifWebGLSupported() { yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2"); yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1"); yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2"); - ok(true, "Highlighting didn't work while blackboxed (2)."); + ok(true, "Highlighting shouldn't work while blackboxed (2)."); ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 1) }); @@ -101,7 +101,7 @@ function ifWebGLSupported() { yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2"); yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1"); yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2"); - ok(true, "Highlighting didn't work while blackboxed (3)."); + ok(true, "Highlighting shouldn't work while blackboxed (3)."); getBlackBoxCheckbox(panel, 0).click(); getBlackBoxCheckbox(panel, 1).click(); @@ -133,9 +133,9 @@ function ifWebGLSupported() { ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 1) }); yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1"); - yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas2"); + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 64, a: 255 }, true, "#canvas2"); yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1"); - yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas2"); + yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 64, a: 255 }, true, "#canvas2"); ok(true, "The second program was correctly highlighted."); ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 1) }); diff --git a/browser/devtools/shadereditor/test/browser_se_programs-blackbox-02.js b/browser/devtools/shadereditor/test/browser_se_programs-blackbox-02.js new file mode 100644 index 000000000000..71202aa6f61f --- /dev/null +++ b/browser/devtools/shadereditor/test/browser_se_programs-blackbox-02.js @@ -0,0 +1,67 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if blackboxing a program works properly in tandem with blended + * overlapping geometry. + */ + +function ifWebGLSupported() { + let [target, debuggee, panel] = yield initShaderEditor(BLENDED_GEOMETRY_CANVAS_URL); + let { gFront, EVENTS, ShadersListView, ShadersEditorsView } = panel.panelWin; + + reload(target); + let firstProgramActor = yield once(gFront, "program-linked"); + let secondProgramActor = yield once(gFront, "program-linked"); + + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 127, b: 127, a: 127 }, true); + ok(true, "The canvas was correctly drawn."); + + getBlackBoxCheckbox(panel, 0).click(); + + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 0, b: 0, a: 127 }, true); + ok(true, "The first program was correctly blackboxed."); + + getBlackBoxCheckbox(panel, 0).click(); + getBlackBoxCheckbox(panel, 1).click(); + + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 127, g: 127, b: 127, a: 255 }, true); + ok(true, "The second program was correctly blackboxed."); + + getBlackBoxCheckbox(panel, 1).click(); + + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 127, b: 127, a: 127 }, true); + ok(true, "The two programs were correctly unblackboxed."); + + getBlackBoxCheckbox(panel, 0).click(); + getBlackBoxCheckbox(panel, 1).click(); + + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 0, b: 0, a: 255 }, true); + ok(true, "The two programs were correctly blackboxed again."); + + getBlackBoxCheckbox(panel, 0).click(); + getBlackBoxCheckbox(panel, 1).click(); + + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 127, b: 127, a: 127 }, true); + ok(true, "The two programs were correctly unblackboxed again."); + + yield teardown(panel); + finish(); +} + +function getBlackBoxCheckbox(aPanel, aIndex) { + return aPanel.panelWin.document.querySelectorAll( + ".side-menu-widget-item-checkbox")[aIndex]; +} + +function once(aTarget, aEvent) { + let deferred = promise.defer(); + aTarget.once(aEvent, deferred.resolve); + return deferred.promise; +} diff --git a/browser/devtools/shadereditor/test/browser_se_programs-highlight.js b/browser/devtools/shadereditor/test/browser_se_programs-highlight-01.js similarity index 95% rename from browser/devtools/shadereditor/test/browser_se_programs-highlight.js rename to browser/devtools/shadereditor/test/browser_se_programs-highlight-01.js index 5e0eb6c35367..6f54f7734bf1 100644 --- a/browser/devtools/shadereditor/test/browser_se_programs-highlight.js +++ b/browser/devtools/shadereditor/test/browser_se_programs-highlight-01.js @@ -47,9 +47,9 @@ function ifWebGLSupported() { ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 1) }); yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1"); - yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas2"); + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 64, a: 255 }, true, "#canvas2"); yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1"); - yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas2"); + yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 64, a: 255 }, true, "#canvas2"); ok(true, "The second program was correctly highlighted."); ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 1) }); diff --git a/browser/devtools/shadereditor/test/browser_se_programs-highlight-02.js b/browser/devtools/shadereditor/test/browser_se_programs-highlight-02.js new file mode 100644 index 000000000000..d5d13464100b --- /dev/null +++ b/browser/devtools/shadereditor/test/browser_se_programs-highlight-02.js @@ -0,0 +1,53 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if highlighting a program works properly in tandem with blended + * overlapping geometry. + */ + +function ifWebGLSupported() { + let [target, debuggee, panel] = yield initShaderEditor(BLENDED_GEOMETRY_CANVAS_URL); + let { gFront, EVENTS, ShadersListView, ShadersEditorsView } = panel.panelWin; + + reload(target); + let firstProgramActor = yield once(gFront, "program-linked"); + let secondProgramActor = yield once(gFront, "program-linked"); + + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 127, b: 127, a: 127 }, true); + ok(true, "The canvas was correctly drawn."); + + ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 0) }); + + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 127, g: 0, b: 32, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 0, b: 32, a: 127 }, true); + ok(true, "The first program was correctly highlighted."); + + ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 0) }); + ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 1) }); + + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 255, g: 0, b: 64, a: 255 }, true); + ok(true, "The second program was correctly highlighted."); + + ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 1) }); + + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 127, b: 127, a: 127 }, true); + ok(true, "The two programs were correctly unhighlighted."); + + yield teardown(panel); + finish(); +} + +function getItemLabel(aPanel, aIndex) { + return aPanel.panelWin.document.querySelectorAll( + ".side-menu-widget-item-label")[aIndex]; +} + +function once(aTarget, aEvent) { + let deferred = promise.defer(); + aTarget.once(aEvent, deferred.resolve); + return deferred.promise; +} diff --git a/browser/devtools/shadereditor/test/browser_webgl-actor-test-06.js b/browser/devtools/shadereditor/test/browser_webgl-actor-test-06.js index 5a516b188d8b..024a6ca7c945 100644 --- a/browser/devtools/shadereditor/test/browser_webgl-actor-test-06.js +++ b/browser/devtools/shadereditor/test/browser_webgl-actor-test-06.js @@ -19,9 +19,9 @@ function ifWebGLSupported() { yield checkShaderSource("The shader sources are correct before highlighting."); ok(true, "The corner pixel colors are correct before highlighting."); - yield programActor.highlight([0, 0, 1, 1]); - yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 255, a: 255 }, true); - yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 0, b: 255, a: 255 }, true); + yield programActor.highlight([0, 1, 0, 1]); + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true); yield checkShaderSource("The shader sources are preserved after highlighting."); ok(true, "The corner pixel colors are correct after highlighting."); diff --git a/browser/devtools/shadereditor/test/browser_webgl-actor-test-09.js b/browser/devtools/shadereditor/test/browser_webgl-actor-test-09.js index 925fde818140..61bd306054c8 100644 --- a/browser/devtools/shadereditor/test/browser_webgl-actor-test-09.js +++ b/browser/devtools/shadereditor/test/browser_webgl-actor-test-09.js @@ -70,9 +70,9 @@ function ifWebGLSupported() { ok(fragSource.contains("vec3 vFragmentColor;"), "The previous correct fragment shader source was preserved."); - yield programActor.highlight([0, 0, 1, 1]); - yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 255, a: 255 }, true); - yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 0, b: 255, a: 255 }, true); + yield programActor.highlight([0, 1, 0, 1]); + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true); ok(true, "Highlighting worked after setting a defective fragment source."); yield programActor.unhighlight(); diff --git a/browser/devtools/shadereditor/test/browser_webgl-actor-test-10.js b/browser/devtools/shadereditor/test/browser_webgl-actor-test-10.js index 070bf6e886e2..f9f4c7f774cc 100644 --- a/browser/devtools/shadereditor/test/browser_webgl-actor-test-10.js +++ b/browser/devtools/shadereditor/test/browser_webgl-actor-test-10.js @@ -27,15 +27,18 @@ function ifWebGLSupported() { function testHighlighting(programActor) { return Task.spawn(function() { yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true); - ok(true, "The top left pixel color was correct before highlighting."); + yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true); + ok(true, "The corner pixel colors are correct before highlighting."); - yield programActor.highlight([0, 0, 1, 1]); - yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 255, a: 255 }, true); - ok(true, "The top left pixel color is correct after highlighting."); + yield programActor.highlight([0, 1, 0, 1]); + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true); + ok(true, "The corner pixel colors are correct after highlighting."); yield programActor.unhighlight(); yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true); - ok(true, "The top left pixel color is correct after unhighlighting."); + yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true); + ok(true, "The corner pixel colors are correct after unhighlighting."); }); } } diff --git a/browser/devtools/shadereditor/test/browser_webgl-actor-test-15.js b/browser/devtools/shadereditor/test/browser_webgl-actor-test-15.js index d26e3f3b5bc1..580d91ffecfa 100644 --- a/browser/devtools/shadereditor/test/browser_webgl-actor-test-15.js +++ b/browser/devtools/shadereditor/test/browser_webgl-actor-test-15.js @@ -81,15 +81,18 @@ function ifWebGLSupported() { function checkHighlightingInTheFirstPage(programActor) { return Task.spawn(function() { yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true); - ok(true, "The top left pixel color was correct before highlighting."); + yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true); + ok(true, "The corner pixel colors are correct before highlighting."); - yield programActor.highlight([0, 0, 1, 1]); - yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 255, a: 255 }, true); - ok(true, "The top left pixel color is correct after highlighting."); + yield programActor.highlight([0, 1, 0, 1]); + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true); + ok(true, "The corner pixel colors are correct after highlighting."); yield programActor.unhighlight(); yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true); - ok(true, "The top left pixel color is correct after unhighlighting."); + yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true); + ok(true, "The corner pixel colors are correct after unhighlighting."); }); } diff --git a/browser/devtools/shadereditor/test/browser_webgl-actor-test-16.js b/browser/devtools/shadereditor/test/browser_webgl-actor-test-16.js index 96b449d2baf1..6fa445c67760 100644 --- a/browser/devtools/shadereditor/test/browser_webgl-actor-test-16.js +++ b/browser/devtools/shadereditor/test/browser_webgl-actor-test-16.js @@ -81,15 +81,18 @@ function ifWebGLSupported() { function checkHighlightingInTheFirstPage(programActor) { return Task.spawn(function() { yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true); - ok(true, "The top left pixel color was correct before highlighting."); + yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true); + ok(true, "The corner pixel colors are correct before highlighting."); - yield programActor.highlight([0, 0, 1, 1]); - yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 255, a: 255 }, true); - ok(true, "The top left pixel color is correct after highlighting."); + yield programActor.highlight([0, 1, 0, 1]); + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true); + ok(true, "The corner pixel colors are correct after highlighting."); yield programActor.unhighlight(); yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true); - ok(true, "The top left pixel color is correct after unhighlighting."); + yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true); + ok(true, "The corner pixel colors are correct after unhighlighting."); }); } diff --git a/browser/devtools/shadereditor/test/doc_blended-geometry.html b/browser/devtools/shadereditor/test/doc_blended-geometry.html new file mode 100644 index 000000000000..7137026effe1 --- /dev/null +++ b/browser/devtools/shadereditor/test/doc_blended-geometry.html @@ -0,0 +1,136 @@ + + + + + + + WebGL editor test page + + + + + + + + + + + + + + + diff --git a/browser/devtools/shadereditor/test/head.js b/browser/devtools/shadereditor/test/head.js index 0da165722282..bd5da2f22399 100644 --- a/browser/devtools/shadereditor/test/head.js +++ b/browser/devtools/shadereditor/test/head.js @@ -28,6 +28,7 @@ const SIMPLE_CANVAS_URL = EXAMPLE_URL + "doc_simple-canvas.html"; const SHADER_ORDER_URL = EXAMPLE_URL + "doc_shader-order.html"; const MULTIPLE_CONTEXTS_URL = EXAMPLE_URL + "doc_multiple-contexts.html"; const OVERLAPPING_GEOMETRY_CANVAS_URL = EXAMPLE_URL + "doc_overlapping-geometry.html"; +const BLENDED_GEOMETRY_CANVAS_URL = EXAMPLE_URL + "doc_blended-geometry.html"; // All tests are asynchronous. waitForExplicitFinish(); diff --git a/browser/devtools/styleeditor/StyleEditorPanel.jsm b/browser/devtools/styleeditor/StyleEditorPanel.jsm index 853205302bfb..ac58b5c26c30 100644 --- a/browser/devtools/styleeditor/StyleEditorPanel.jsm +++ b/browser/devtools/styleeditor/StyleEditorPanel.jsm @@ -100,7 +100,7 @@ StyleEditorPanel.prototype = { return; } let stylesheet = this._debuggee.styleSheetFromHref(href); - this.UI.selectStyleSheet(href, line - 1, col - 1); + this.UI.selectStyleSheet(href, line - 1, col ? col - 1 : 0); }, /** diff --git a/browser/devtools/styleeditor/test/browser.ini b/browser/devtools/styleeditor/test/browser.ini index 303a11d650d6..81cd0e300420 100644 --- a/browser/devtools/styleeditor/test/browser.ini +++ b/browser/devtools/styleeditor/test/browser.ini @@ -39,3 +39,4 @@ support-files = [browser_styleeditor_reload.js] [browser_styleeditor_sv_keynav.js] [browser_styleeditor_sv_resize.js] +[browser_styleeditor_selectstylesheet.js] \ No newline at end of file diff --git a/browser/devtools/styleeditor/test/browser_styleeditor_selectstylesheet.js b/browser/devtools/styleeditor/test/browser_styleeditor_selectstylesheet.js new file mode 100644 index 000000000000..e1f129a0e9f1 --- /dev/null +++ b/browser/devtools/styleeditor/test/browser_styleeditor_selectstylesheet.js @@ -0,0 +1,54 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const TESTCASE_URI = TEST_BASE_HTTPS + "simple.html"; +const NEW_URI = TEST_BASE_HTTPS + "media.html"; + +const LINE_NO = 5; +const COL_NO = 0; + +let gContentWin; +let gUI; + +function test() +{ + waitForExplicitFinish(); + + addTabAndOpenStyleEditor(function(panel) { + gContentWin = gBrowser.selectedTab.linkedBrowser.contentWindow.wrappedJSObject; + gUI = panel.UI; + + let count = 0; + gUI.on("editor-added", function editorAdded(event, editor) { + if (++count == 2) { + gUI.off("editor-added", editorAdded); + gUI.editors[0].getSourceEditor().then(runTests); + } + }) + }); + + content.location = TESTCASE_URI; +} + +function runTests() +{ + let count = 0; + + // Make sure Editor doesn't go into an infinite loop when + // column isn't passed. See bug 941018. + gUI.once("editor-selected", (event, editor) => { + editor.getSourceEditor().then(() => { + is(gUI.selectedEditor, gUI.editors[1], "second editor is selected"); + let {line, ch} = gUI.selectedEditor.sourceEditor.getCursor(); + + is(line, LINE_NO, "correct line selected"); + is(ch, COL_NO, "correct column selected"); + + gUI = null; + finish(); + }); + }); + + gUI.selectStyleSheet(gUI.editors[1].styleSheet.href, LINE_NO); +} \ No newline at end of file diff --git a/browser/devtools/webconsole/hudservice.js b/browser/devtools/webconsole/hudservice.js index bab136605363..9c896f628dca 100644 --- a/browser/devtools/webconsole/hudservice.js +++ b/browser/devtools/webconsole/hudservice.js @@ -28,6 +28,8 @@ const BROWSER_CONSOLE_WINDOW_FEATURES = "chrome,titlebar,toolbar,centerscreen,re // The preference prefix for all of the Browser Console filters. const BROWSER_CONSOLE_FILTER_PREFS_PREFIX = "devtools.browserconsole.filter."; +let gHudId = 0; + /////////////////////////////////////////////////////////////////////////// //// The HUD service @@ -301,7 +303,7 @@ function WebConsole(aTarget, aIframeWindow, aChromeWindow) { this.iframeWindow = aIframeWindow; this.chromeWindow = aChromeWindow; - this.hudId = "hud_" + Date.now(); + this.hudId = "hud_" + ++gHudId; this.target = aTarget; this.browserWindow = this.chromeWindow.top; diff --git a/browser/devtools/webconsole/test/browser.ini b/browser/devtools/webconsole/test/browser.ini index 5f0fdc6fedc3..c7e78fe31a14 100644 --- a/browser/devtools/webconsole/test/browser.ini +++ b/browser/devtools/webconsole/test/browser.ini @@ -93,6 +93,7 @@ support-files = test_bug_770099_bad_policy_uri.html^headers^ test_bug_770099_violation.html test_bug_770099_violation.html^headers^ + test-autocomplete-in-stackframe.html testscript.js test-bug_923281_console_log_filter.html test-bug_923281_test1.js @@ -241,3 +242,4 @@ skip-if = os == "linux" [browser_webconsole_reflow.js] [browser_webconsole_log_file_filter.js] [browser_webconsole_expandable_timestamps.js] +[browser_webconsole_autocomplete_in_debugger_stackframe.js] diff --git a/browser/devtools/webconsole/test/browser_webconsole_autocomplete_in_debugger_stackframe.js b/browser/devtools/webconsole/test/browser_webconsole_autocomplete_in_debugger_stackframe.js new file mode 100644 index 000000000000..9acefa4c9f6d --- /dev/null +++ b/browser/devtools/webconsole/test/browser_webconsole_autocomplete_in_debugger_stackframe.js @@ -0,0 +1,212 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// Test that makes sure web console autocomplete happens in the user-selected stackframe +// from the js debugger. + +const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-autocomplete-in-stackframe.html"; + +let testDriver, gStackframes; + +function test() +{ + requestLongerTimeout(2); + addTab(TEST_URI); + browser.addEventListener("load", function onLoad() { + browser.removeEventListener("load", onLoad, true); + openConsole(null, function(hud) { + testDriver = testCompletion(hud); + testDriver.next(); + }); + }, true); +} + +function testNext() { + executeSoon(function() { + testDriver.next(); + }); +} + +function testCompletion(hud) { + let jsterm = hud.jsterm; + let input = jsterm.inputNode; + let popup = jsterm.autocompletePopup; + + // Test if 'f' gives 'foo1' but not 'foo2' or 'foo3' + input.value = "f"; + input.setSelectionRange(1, 1); + jsterm.complete(jsterm.COMPLETE_HINT_ONLY, testNext); + yield undefined; + + let newItems = popup.getItems(); + ok(newItems.length > 0, "'f' gave a list of suggestions"); + ok(!newItems.every(function(item) { + return item.label != "foo1"; + }), "autocomplete results do contain foo1"); + ok(!newItems.every(function(item) { + return item.label != "foo1Obj"; + }), "autocomplete results do contain foo1Obj"); + ok(newItems.every(function(item) { + return item.label != "foo2"; + }), "autocomplete results do not contain foo2"); + ok(newItems.every(function(item) { + return item.label != "foo2Obj"; + }), "autocomplete results do not contain foo2Obj"); + ok(newItems.every(function(item) { + return item.label != "foo3"; + }), "autocomplete results do not contain foo3"); + ok(newItems.every(function(item) { + return item.label != "foo3Obj"; + }), "autocomplete results do not contain foo3Obj"); + + // Test if 'foo1Obj.' gives 'prop1' and 'prop2' + input.value = "foo1Obj."; + input.setSelectionRange(8, 8); + jsterm.complete(jsterm.COMPLETE_HINT_ONLY, testNext); + yield undefined; + + newItems = popup.getItems(); + ok(!newItems.every(function(item) { + return item.label != "prop1"; + }), "autocomplete results do contain prop1"); + ok(!newItems.every(function(item) { + return item.label != "prop2"; + }), "autocomplete results do contain prop2"); + + // Test if 'foo1Obj.prop2.' gives 'prop21' + input.value = "foo1Obj.prop2."; + input.setSelectionRange(14, 14); + jsterm.complete(jsterm.COMPLETE_HINT_ONLY, testNext); + yield undefined; + + newItems = popup.getItems(); + ok(!newItems.every(function(item) { + return item.label != "prop21"; + }), "autocomplete results do contain prop21"); + + info("openDebugger"); + executeSoon(() => openDebugger().then(debuggerOpened)); + yield undefined; + + // From this point on the + // Test if 'f' gives 'foo3' and 'foo1' but not 'foo2' + input.value = "f"; + input.setSelectionRange(1, 1); + jsterm.complete(jsterm.COMPLETE_HINT_ONLY, testNext); + yield undefined; + + newItems = popup.getItems(); + ok(newItems.length > 0, "'f' gave a list of suggestions"); + ok(!newItems.every(function(item) { + return item.label != "foo3"; + }), "autocomplete results do contain foo3"); + ok(!newItems.every(function(item) { + return item.label != "foo3Obj"; + }), "autocomplete results do contain foo3Obj"); + ok(!newItems.every(function(item) { + return item.label != "foo1"; + }), "autocomplete results do contain foo1"); + ok(!newItems.every(function(item) { + return item.label != "foo1Obj"; + }), "autocomplete results do contain foo1Obj"); + ok(newItems.every(function(item) { + return item.label != "foo2"; + }), "autocomplete results do not contain foo2"); + ok(newItems.every(function(item) { + return item.label != "foo2Obj"; + }), "autocomplete results do not contain foo2Obj"); + + openDebugger().then(() => { + gStackframes.selectFrame(1); + + info("openConsole"); + executeSoon(() => openConsole(null, () => testDriver.next())); + }); + yield undefined; + + // Test if 'f' gives 'foo2' and 'foo1' but not 'foo3' + input.value = "f"; + input.setSelectionRange(1, 1); + jsterm.complete(jsterm.COMPLETE_HINT_ONLY, testNext); + yield undefined; + + newItems = popup.getItems(); + ok(newItems.length > 0, "'f' gave a list of suggestions"); + ok(!newItems.every(function(item) { + return item.label != "foo2"; + }), "autocomplete results do contain foo2"); + ok(!newItems.every(function(item) { + return item.label != "foo2Obj"; + }), "autocomplete results do contain foo2Obj"); + ok(!newItems.every(function(item) { + return item.label != "foo1"; + }), "autocomplete results do contain foo1"); + ok(!newItems.every(function(item) { + return item.label != "foo1Obj"; + }), "autocomplete results do contain foo1Obj"); + ok(newItems.every(function(item) { + return item.label != "foo3"; + }), "autocomplete results do not contain foo3"); + ok(newItems.every(function(item) { + return item.label != "foo3Obj"; + }), "autocomplete results do not contain foo3Obj"); + + // Test if 'foo2Obj.' gives 'prop1' + input.value = "foo2Obj."; + input.setSelectionRange(8, 8); + jsterm.complete(jsterm.COMPLETE_HINT_ONLY, testNext); + yield undefined; + + newItems = popup.getItems(); + ok(!newItems.every(function(item) { + return item.label != "prop1"; + }), "autocomplete results do contain prop1"); + + // Test if 'foo1Obj.prop1.' gives 'prop11' + input.value = "foo2Obj.prop1."; + input.setSelectionRange(14, 14); + jsterm.complete(jsterm.COMPLETE_HINT_ONLY, testNext); + yield undefined; + + newItems = popup.getItems(); + ok(!newItems.every(function(item) { + return item.label != "prop11"; + }), "autocomplete results do contain prop11"); + + // Test if 'foo1Obj.prop1.prop11.' gives suggestions for a string i.e. 'length' + input.value = "foo2Obj.prop1.prop11."; + input.setSelectionRange(21, 21); + jsterm.complete(jsterm.COMPLETE_HINT_ONLY, testNext); + yield undefined; + + newItems = popup.getItems(); + ok(!newItems.every(function(item) { + return item.label != "length"; + }), "autocomplete results do contain length"); + + testDriver = null; + executeSoon(finishTest); + yield undefined; +} + +function debuggerOpened(aResult) +{ + let debuggerWin = aResult.panelWin; + let debuggerController = debuggerWin.DebuggerController; + let thread = debuggerController.activeThread; + gStackframes = debuggerController.StackFrames; + + executeSoon(() => { + thread.addOneTimeListener("framesadded", onFramesAdded); + info("firstCall()"); + content.wrappedJSObject.firstCall(); + }); +} + +function onFramesAdded() +{ + info("onFramesAdded, openConsole() now"); + executeSoon(() => openConsole(null, testNext)); +} diff --git a/browser/devtools/webconsole/test/browser_webconsole_bug_585991_autocomplete_keys.js b/browser/devtools/webconsole/test/browser_webconsole_bug_585991_autocomplete_keys.js index 030eea6b88fd..90ea5dcfea81 100644 --- a/browser/devtools/webconsole/test/browser_webconsole_bug_585991_autocomplete_keys.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_585991_autocomplete_keys.js @@ -18,14 +18,14 @@ function consoleOpened(aHud) { HUD = aHud; info("web console opened"); - content.wrappedJSObject.foobarBug585991 = { - "item0": "value0", - "item1": "value1", - "item2": "value2", - "item3": "value3", - }; - jsterm = HUD.jsterm; + + jsterm.execute("window.foobarBug585991={" + + "'item0': 'value0'," + + "'item1': 'value1'," + + "'item2': 'value2'," + + "'item3': 'value3'" + + "}"); popup = jsterm.autocompletePopup; completeNode = jsterm.completeNode; inputNode = jsterm.inputNode; diff --git a/browser/devtools/webconsole/test/browser_webconsole_bug_632347_iterators_generators.js b/browser/devtools/webconsole/test/browser_webconsole_bug_632347_iterators_generators.js index b4a0e9b9daf0..776f8aeb01f0 100644 --- a/browser/devtools/webconsole/test/browser_webconsole_bug_632347_iterators_generators.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_632347_iterators_generators.js @@ -19,20 +19,25 @@ function consoleOpened(HUD) { let tools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools; let JSPropertyProvider = tools.require("devtools/toolkit/webconsole/utils").JSPropertyProvider; + let tmp = Cu.import("resource://gre/modules/jsdebugger.jsm", {}); + tmp.addDebuggerToGlobal(tmp); + let dbg = new tmp.Debugger; + let jsterm = HUD.jsterm; let win = content.wrappedJSObject; + let dbgWindow = dbg.makeGlobalObjectReference(win); // Make sure autocomplete does not walk through iterators and generators. let result = win.gen1.next(); - let completion = JSPropertyProvider(win, "gen1."); - is(completion, null, "no matches for gen1"); + let completion = JSPropertyProvider(dbgWindow, null, "gen1."); + isnot(completion.matches.length, 0, "Got matches for gen1"); is(result+1, win.gen1.next(), "gen1.next() did not execute"); result = win.gen2.next(); - completion = JSPropertyProvider(win, "gen2."); - is(completion, null, "no matches for gen2"); + completion = JSPropertyProvider(dbgWindow, null, "gen2."); + isnot(completion.matches.length, 0, "Got matches for gen2"); is((result/2+1)*2, win.gen2.next(), "gen2.next() did not execute"); @@ -41,17 +46,18 @@ function consoleOpened(HUD) { is(result[0], "foo", "iter1.next() [0] is correct"); is(result[1], "bar", "iter1.next() [1] is correct"); - completion = JSPropertyProvider(win, "iter1."); - is(completion, null, "no matches for iter1"); + completion = JSPropertyProvider(dbgWindow, null, "iter1."); + isnot(completion.matches.length, 0, "Got matches for iter1"); result = win.iter1.next(); is(result[0], "baz", "iter1.next() [0] is correct"); is(result[1], "baaz", "iter1.next() [1] is correct"); - completion = JSPropertyProvider(content, "iter2."); - is(completion, null, "no matches for iter2"); + let dbgContent = dbg.makeGlobalObjectReference(content); + completion = JSPropertyProvider(dbgContent, null, "iter2."); + isnot(completion.matches.length, 0, "Got matches for iter2"); - completion = JSPropertyProvider(win, "window."); + completion = JSPropertyProvider(dbgWindow, null, "window."); ok(completion, "matches available for window"); ok(completion.matches.length, "matches available for window (length)"); diff --git a/browser/devtools/webconsole/test/browser_webconsole_property_provider.js b/browser/devtools/webconsole/test/browser_webconsole_property_provider.js index 644345de4b33..2ca742d48178 100644 --- a/browser/devtools/webconsole/test/browser_webconsole_property_provider.js +++ b/browser/devtools/webconsole/test/browser_webconsole_property_provider.js @@ -18,17 +18,22 @@ function testPropertyProvider() { let tools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools; let JSPropertyProvider = tools.require("devtools/toolkit/webconsole/utils").JSPropertyProvider; - let completion = JSPropertyProvider(content, "thisIsNotDefined"); + let tmp = Cu.import("resource://gre/modules/jsdebugger.jsm", {}); + tmp.addDebuggerToGlobal(tmp); + let dbg = new tmp.Debugger; + let dbgWindow = dbg.makeGlobalObjectReference(content); + + let completion = JSPropertyProvider(dbgWindow, null, "thisIsNotDefined"); is (completion.matches.length, 0, "no match for 'thisIsNotDefined"); // This is a case the PropertyProvider can't handle. Should return null. - completion = JSPropertyProvider(content, "window[1].acb"); + completion = JSPropertyProvider(dbgWindow, null, "window[1].acb"); is (completion, null, "no match for 'window[1].acb"); // A very advanced completion case. var strComplete = 'function a() { }document;document.getElementById(window.locatio'; - completion = JSPropertyProvider(content, strComplete); + completion = JSPropertyProvider(dbgWindow, null, strComplete); ok(completion.matches.length == 2, "two matches found"); ok(completion.matchProp == "locatio", "matching part is 'test'"); var matches = completion.matches; diff --git a/browser/devtools/webconsole/test/test-autocomplete-in-stackframe.html b/browser/devtools/webconsole/test/test-autocomplete-in-stackframe.html new file mode 100644 index 000000000000..ba5212de33a3 --- /dev/null +++ b/browser/devtools/webconsole/test/test-autocomplete-in-stackframe.html @@ -0,0 +1,50 @@ + + + + + + Test for bug 842682 - use the debugger API for web console autocomplete + + + +

Hello world!

+ + diff --git a/browser/devtools/webconsole/webconsole.js b/browser/devtools/webconsole/webconsole.js index 38606ddabbfb..9c2a6f8c3485 100644 --- a/browser/devtools/webconsole/webconsole.js +++ b/browser/devtools/webconsole/webconsole.js @@ -3074,6 +3074,14 @@ JSTerm.prototype = { */ _autocompleteQuery: null, + /** + * The frameActorId used in the last autocomplete query. Whenever this changes + * the autocomplete cache must be invalidated. + * @private + * @type string + */ + _lastFrameActorId: null, + /** * The Web Console sidebar. * @see this._createSidebar() @@ -4286,6 +4294,8 @@ JSTerm.prototype = { { let inputNode = this.inputNode; let inputValue = inputNode.value; + let frameActor = this.getFrameActor(this.SELECTED_FRAME); + // If the inputNode has no value, then don't try to complete on it. if (!inputValue) { this.clearCompletion(); @@ -4299,7 +4309,7 @@ JSTerm.prototype = { } // Update the completion results. - if (this.lastCompletion.value != inputValue) { + if (this.lastCompletion.value != inputValue || frameActor != this._lastFrameActorId) { this._updateCompletionResult(aType, aCallback); return false; } @@ -4335,7 +4345,8 @@ JSTerm.prototype = { _updateCompletionResult: function JST__updateCompletionResult(aType, aCallback) { - if (this.lastCompletion.value == this.inputNode.value) { + let frameActor = this.getFrameActor(this.SELECTED_FRAME); + if (this.lastCompletion.value == this.inputNode.value && frameActor == this._lastFrameActorId) { return; } @@ -4350,7 +4361,7 @@ JSTerm.prototype = { // character we ask the server again for suggestions. // Check if last character is non-alphanumeric - if (!/[a-zA-Z0-9]$/.test(input)) { + if (!/[a-zA-Z0-9]$/.test(input) || frameActor != this._lastFrameActorId) { this._autocompleteQuery = null; this._autocompleteCache = null; } @@ -4380,6 +4391,8 @@ JSTerm.prototype = { return; } + this._lastFrameActorId = frameActor; + this.lastCompletion = { requestId: requestId, completionType: aType, @@ -4388,7 +4401,8 @@ JSTerm.prototype = { let callback = this._receiveAutocompleteProperties.bind(this, requestId, aCallback); - this.webConsoleClient.autocomplete(input, cursor, callback); + + this.webConsoleClient.autocomplete(input, cursor, callback, frameActor); }, /** diff --git a/browser/extensions/shumway/content/ShumwayStreamConverter.jsm b/browser/extensions/shumway/content/ShumwayStreamConverter.jsm index 5eaab67e0ab6..4e65474b7203 100644 --- a/browser/extensions/shumway/content/ShumwayStreamConverter.jsm +++ b/browser/extensions/shumway/content/ShumwayStreamConverter.jsm @@ -72,7 +72,8 @@ function log(aMsg) { } function getDOMWindow(aChannel) { - var requestor = aChannel.notificationCallbacks; + var requestor = aChannel.notificationCallbacks || + aChannel.loadGroup.notificationCallbacks; var win = requestor.getInterface(Components.interfaces.nsIDOMWindow); return win; } @@ -177,7 +178,10 @@ function isShumwayEnabledFor(actions) { // blacklisting well known sites with issues if (/\.ytimg\.com\//i.test(url) /* youtube movies */ || - /\/vui.swf\b/i.test(url) /* vidyo manager */ ) { + /\/vui.swf\b/i.test(url) /* vidyo manager */ || + /soundcloud\.com\/player\/assets\/swf/i.test(url) /* soundcloud */ || + /sndcdn\.com\/assets\/swf/.test(url) /* soundcloud */ || + /vimeocdn\.com/.test(url) /* vimeo */) { return false; } @@ -727,34 +731,45 @@ ShumwayStreamConverterBase.prototype = { throw Cr.NS_ERROR_NOT_IMPLEMENTED; }, - isValidRequest: function() { - return true; - }, - getUrlHint: function(requestUrl) { return requestUrl.spec; }, createChromeActions: function(window, document, urlHint) { - var url; + var url = urlHint; var baseUrl; var pageUrl; var element = window.frameElement; var isOverlay = false; var objectParams = {}; if (element) { - var tagName = element.nodeName; + // PlayPreview overlay "belongs" to the embed/object tag and consists of + // DIV and IFRAME. Starting from IFRAME and looking for first object tag. + var tagName = element.nodeName, containerElement; while (tagName != 'EMBED' && tagName != 'OBJECT') { // plugin overlay skipping until the target plugin is found isOverlay = true; + containerElement = element; element = element.parentNode; - if (!element) - throw 'Plugin element is not found'; + if (!element) { + throw new Error('Plugin element is not found'); + } tagName = element.nodeName; } - // TODO: remove hack once bug 920927 is fixed - element.style.visibility = 'visible'; + if (isOverlay) { + // Checking if overlay is a proper PlayPreview overlay. + for (var i = 0; i < element.children.length; i++) { + if (element.children[i] === containerElement) { + throw new Error('Plugin element is invalid'); + } + } + } + } + + if (element) { + // Getting absolute URL from the EMBED tag + url = element.srcURI.spec; pageUrl = element.ownerDocument.location.href; // proper page url? @@ -764,7 +779,6 @@ ShumwayStreamConverterBase.prototype = { objectParams[paramName] = element.attributes[i].value; } } else { - url = element.getAttribute('data'); for (var i = 0; i < element.childNodes.length; ++i) { var paramElement = element.childNodes[i]; if (paramElement.nodeType != 1 || @@ -777,7 +791,10 @@ ShumwayStreamConverterBase.prototype = { } } - url = url || objectParams.src || objectParams.movie; + if (!url) { // at this point url shall be known -- asserting + throw new Error('Movie url is not specified'); + } + baseUrl = objectParams.base || pageUrl; var movieParams = {}; @@ -794,9 +811,6 @@ ShumwayStreamConverterBase.prototype = { } } - url = !url ? urlHint : Services.io.newURI(url, null, - baseUrl ? Services.io.newURI(baseUrl, null, null) : null).spec; - var allowScriptAccess = false; switch (objectParams.allowscriptaccess || 'sameDomain') { case 'always': @@ -830,9 +844,6 @@ ShumwayStreamConverterBase.prototype = { // nsIStreamConverter::asyncConvertData asyncConvertData: function(aFromType, aToType, aListener, aCtxt) { - if(!this.isValidRequest(aCtxt)) - throw Cr.NS_ERROR_NOT_IMPLEMENTED; - // Store the listener passed to us this.listener = aListener; }, @@ -847,8 +858,15 @@ ShumwayStreamConverterBase.prototype = { onStartRequest: function(aRequest, aContext) { // Setup the request so we can use it below. aRequest.QueryInterface(Ci.nsIChannel); - // Cancel the request so the viewer can handle it. - aRequest.cancel(Cr.NS_BINDING_ABORTED); + + aRequest.QueryInterface(Ci.nsIWritablePropertyBag); + + // Change the content type so we don't get stuck in a loop. + aRequest.setProperty('contentType', aRequest.contentType); + aRequest.contentType = 'text/html'; + + // TODO For now suspending request, however we can continue fetching data + aRequest.suspend(); var originalURI = aRequest.URI; @@ -857,61 +875,74 @@ ShumwayStreamConverterBase.prototype = { getBoolPref('shumway.simpleMode', false); // Create a new channel that loads the viewer as a resource. - var channel = Services.io.newChannel(isSimpleMode ? + var viewerUrl = isSimpleMode ? 'resource://shumway/web/simple.html' : - 'resource://shumway/web/viewer.html', null, null); + 'resource://shumway/web/viewer.html'; + var channel = Services.io.newChannel(viewerUrl, null, null); var converter = this; var listener = this.listener; // Proxy all the request observer calls, when it gets to onStopRequest // we can get the dom window. var proxy = { - onStartRequest: function() { - listener.onStartRequest.apply(listener, arguments); + onStartRequest: function(request, context) { + listener.onStartRequest(aRequest, context); }, - onDataAvailable: function() { - listener.onDataAvailable.apply(listener, arguments); + onDataAvailable: function(request, context, inputStream, offset, count) { + listener.onDataAvailable(aRequest, context, inputStream, offset, count); }, - onStopRequest: function() { + onStopRequest: function(request, context, statusCode) { + // Cancel the request so the viewer can handle it. + aRequest.resume(); + aRequest.cancel(Cr.NS_BINDING_ABORTED); + var domWindow = getDOMWindow(channel); - if (domWindow.document.documentURIObject.equals(channel.originalURI)) { - // Double check the url is still the correct one. - let actions = converter.createChromeActions(domWindow, - domWindow.document, - converter.getUrlHint(originalURI)); - if (!isShumwayEnabledFor(actions)) { - actions.fallback(true); - return; - } + let actions = converter.createChromeActions(domWindow, + domWindow.document, + converter.getUrlHint(originalURI)); - // Report telemetry on amount of swfs on the page - if (actions.isOverlay) { - // Looking for last actions with same baseUrl - var prevPageActions = ActivationQueue.findLastOnPage(actions.baseUrl); - var pageIndex = !prevPageActions ? 1 : (prevPageActions.telemetry.pageIndex + 1); - actions.telemetry.pageIndex = pageIndex; - ShumwayTelemetry.onPageIndex(pageIndex); - } else { - ShumwayTelemetry.onPageIndex(0); - } - - actions.activationCallback = function(domWindow, isSimpleMode) { - delete this.activationCallback; - activateShumwayScripts(domWindow, isSimpleMode); - }.bind(actions, domWindow, isSimpleMode); - ActivationQueue.enqueue(actions); - - let requestListener = new RequestListener(actions); - domWindow.addEventListener('shumway.message', function(event) { - requestListener.receive(event); - }, false, true); + if (!isShumwayEnabledFor(actions)) { + actions.fallback(true); + return; } - listener.onStopRequest.apply(listener, arguments); + + // Report telemetry on amount of swfs on the page + if (actions.isOverlay) { + // Looking for last actions with same baseUrl + var prevPageActions = ActivationQueue.findLastOnPage(actions.baseUrl); + var pageIndex = !prevPageActions ? 1 : (prevPageActions.telemetry.pageIndex + 1); + actions.telemetry.pageIndex = pageIndex; + ShumwayTelemetry.onPageIndex(pageIndex); + } else { + ShumwayTelemetry.onPageIndex(0); + } + + actions.activationCallback = function(domWindow, isSimpleMode) { + delete this.activationCallback; + activateShumwayScripts(domWindow, isSimpleMode); + }.bind(actions, domWindow, isSimpleMode); + ActivationQueue.enqueue(actions); + + let requestListener = new RequestListener(actions); + domWindow.addEventListener('shumway.message', function(event) { + requestListener.receive(event); + }, false, true); + + listener.onStopRequest(aRequest, context, statusCode); } }; - // XXX? Keep the URL the same so the browser sees it as the same. - // channel.originalURI = aRequest.URI; + // Keep the URL the same so the browser sees it as the same. + channel.originalURI = aRequest.URI; + channel.loadGroup = aRequest.loadGroup; + + // We can use resource principal when data is fetched by the chrome + // e.g. useful for NoScript + var securityManager = Cc['@mozilla.org/scriptsecuritymanager;1'] + .getService(Ci.nsIScriptSecurityManager); + var uri = Services.io.newURI(viewerUrl, null, null); + var resourcePrincipal = securityManager.getNoAppCodebasePrincipal(uri); + aRequest.owner = resourcePrincipal; channel.asyncOpen(proxy, aContext); }, @@ -943,17 +974,6 @@ copyProperties(ShumwayStreamOverlayConverter.prototype, { classDescription: 'Shumway PlayPreview Component', contractID: '@mozilla.org/streamconv;1?from=application/x-moz-playpreview&to=*/*' }); -ShumwayStreamOverlayConverter.prototype.isValidRequest = - (function(aCtxt) { - try { - var request = aCtxt; - request.QueryInterface(Ci.nsIChannel); - var spec = request.URI.spec; - return spec.indexOf(EXPECTED_PLAYPREVIEW_URI_PREFIX) === 0; - } catch (e) { - return false; - } - }); ShumwayStreamOverlayConverter.prototype.getUrlHint = function (requestUrl) { return ''; }; diff --git a/browser/extensions/shumway/content/avm2/generated/avm1lib/avm1lib.abc b/browser/extensions/shumway/content/avm2/generated/avm1lib/avm1lib.abc index 0f9c67914b51..cfa75d6d6ac0 100644 Binary files a/browser/extensions/shumway/content/avm2/generated/avm1lib/avm1lib.abc and b/browser/extensions/shumway/content/avm2/generated/avm1lib/avm1lib.abc differ diff --git a/browser/extensions/shumway/content/avm2/generated/builtin/builtin.abc b/browser/extensions/shumway/content/avm2/generated/builtin/builtin.abc index 28ccc19e9f2b..53a0322aad8d 100644 Binary files a/browser/extensions/shumway/content/avm2/generated/builtin/builtin.abc and b/browser/extensions/shumway/content/avm2/generated/builtin/builtin.abc differ diff --git a/browser/extensions/shumway/content/shumway-worker.js b/browser/extensions/shumway/content/shumway-worker.js index 63af10ea8eec..8fdbf8fbc76a 100644 --- a/browser/extensions/shumway/content/shumway-worker.js +++ b/browser/extensions/shumway/content/shumway-worker.js @@ -156,6 +156,25 @@ throw new Error(msg); } }(this)); +var create = Object.create; +var defineProperty = Object.defineProperty; +var keys = Object.keys; +var isArray = Array.isArray; +var fromCharCode = String.fromCharCode; +var logE = Math.log; +var max = Math.max; +var min = Math.min; +var pow = Math.pow; +var push = Array.prototype.push; +var slice = Array.prototype.slice; +var splice = Array.prototype.splice; +function fail(msg, context) { + throw new Error((context ? context + ': ' : '') + msg); +} +function assert(cond, msg, context) { + if (!cond) + fail(msg, context); +} function scriptProperties(namespace, props) { return props.reduce(function (o, p) { o[p] = namespace + ' ' + p; @@ -168,10 +187,6 @@ function cloneObject(obj) { clone[prop] = obj[prop]; return clone; } -function throwError(name, error) { - var message = formatErrorMessage.apply(null, slice.call(arguments, 1)); - throwErrorFromVM(AVM2.currentDomain(), name, message, error.code); -} function sortByDepth(a, b) { var levelA = a._level; var levelB = b._level; @@ -198,6 +213,89 @@ function sortByDepth(a, b) { function sortNumeric(a, b) { return a - b; } +function rgbaObjToStr(color) { + return 'rgba(' + color.red + ',' + color.green + ',' + color.blue + ',' + color.alpha / 255 + ')'; +} +function rgbIntAlphaToStr(color, alpha) { + color |= 0; + if (alpha >= 1) { + var colorStr = color.toString(16); + while (colorStr.length < 6) { + colorStr = '0' + colorStr; + } + return '#' + colorStr; + } + var red = color >> 16 & 255; + var green = color >> 8 & 255; + var blue = color & 255; + return 'rgba(' + red + ',' + green + ',' + blue + ',' + alpha + ')'; +} +function argbUintToStr(argb) { + return 'rgba(' + (argb >>> 16 & 255) + ',' + (argb >>> 8 & 255) + ',' + (argb & 255) + ',' + (argb >>> 24 & 255) / 255 + ')'; +} +(function functionNameSupport() { + if (eval('function t() {} t.name === \'t\'')) { + return; + } + Object.defineProperty(Function.prototype, 'name', { + get: function () { + if (this.__name) { + return this.__name; + } + var m = /function\s([^\(]+)/.exec(this.toString()); + var name = m && m[1] !== 'anonymous' ? m[1] : null; + this.__name = name; + return name; + }, + configurable: true, + enumerable: false + }); +}()); +var randomStyleCache; +var nextStyle = 0; +function randomStyle() { + if (!randomStyleCache) { + randomStyleCache = [ + '#ff5e3a', + '#ff9500', + '#ffdb4c', + '#87fc70', + '#52edc7', + '#1ad6fd', + '#c644fc', + '#ef4db6', + '#4a4a4a', + '#dbddde', + '#ff3b30', + '#ff9500', + '#ffcc00', + '#4cd964', + '#34aadc', + '#007aff', + '#5856d6', + '#ff2d55', + '#8e8e93', + '#c7c7cc', + '#5ad427', + '#c86edf', + '#d1eefc', + '#e0f8d8', + '#fb2b69', + '#f7f7f7', + '#1d77ef', + '#d6cec3', + '#55efcb', + '#ff4981', + '#ffd3e0', + '#f7f7f7', + '#ff1300', + '#1f1f21', + '#bdbec2', + '#ff3a2d' + ]; + } + return randomStyleCache[nextStyle++ % randomStyleCache.length]; +} var Promise = function PromiseClosure() { function isPromise(obj) { return typeof obj === 'object' && obj !== null && typeof obj.then === 'function'; @@ -450,73 +548,6 @@ if (!this.performance) { if (!this.performance.now) { this.performance.now = Date.now; } -var create = Object.create; -var defineProperty = Object.defineProperty; -var keys = Object.keys; -var isArray = Array.isArray; -var fromCharCode = String.fromCharCode; -var logE = Math.log; -var max = Math.max; -var min = Math.min; -var pow = Math.pow; -var push = Array.prototype.push; -var slice = Array.prototype.slice; -var splice = Array.prototype.splice; -function fail(msg, context) { - throw new Error((context ? context + ': ' : '') + msg); -} -function assert(cond, msg, context) { - if (!cond) - fail(msg, context); -} -function rgbaObjToStr(color) { - return 'rgba(' + color.red + ',' + color.green + ',' + color.blue + ',' + color.alpha / 255 + ')'; -} -function rgbIntAlphaToStr(color, alpha) { - color |= 0; - if (alpha >= 1) { - var colorStr = color.toString(16); - while (colorStr.length < 6) { - colorStr = '0' + colorStr; - } - return '#' + colorStr; - } - var red = color >> 16 & 255; - var green = color >> 8 & 255; - var blue = color & 255; - return 'rgba(' + red + ',' + green + ',' + blue + ',' + alpha + ')'; -} -function argbUintToStr(argb) { - return 'rgba(' + (argb >>> 16 & 255) + ',' + (argb >>> 8 & 255) + ',' + (argb & 255) + ',' + (argb >>> 24 & 255) / 255 + ')'; -} -(function functionNameSupport() { - if (eval('function t() {} t.name === \'t\'')) { - return; - } - Object.defineProperty(Function.prototype, 'name', { - get: function () { - if (this.__name) { - return this.__name; - } - var m = /function\s([^\(]+)/.exec(this.toString()); - var name = m && m[1] !== 'anonymous' ? m[1] : null; - this.__name = name; - return name; - }, - configurable: true, - enumerable: false - }); -}()); -var randomStyleCache; -function randomStyle() { - if (!randomStyleCache) { - randomStyleCache = []; - for (var i = 0; i < 50; i++) { - randomStyleCache.push('#' + ('00000' + (Math.random() * (1 << 24) | 0).toString(16)).slice(-6)); - } - } - return randomStyleCache[Math.random() * randomStyleCache.length | 0]; -} var SWF_TAG_CODE_CSM_TEXT_SETTINGS = 74; var SWF_TAG_CODE_DEFINE_BINARY_DATA = 87; var SWF_TAG_CODE_DEFINE_BITS = 6; @@ -1364,7 +1395,7 @@ function applySegmentToStyles(segment, styles, linePaths, fillPaths, isMorph) { path.addSegment(commands, data, morphData); } } -function convertRecordsToStyledPaths(records, fillPaths, linePaths, dictionary, dependencies, recordsMorph) { +function convertRecordsToStyledPaths(records, fillPaths, linePaths, dictionary, dependencies, recordsMorph, transferables) { var isMorph = recordsMorph !== null; var styles = { fill0: 0, @@ -1372,8 +1403,8 @@ function convertRecordsToStyledPaths(records, fillPaths, linePaths, dictionary, line: 0 }; var segment = null; - var allFillPaths = fillPaths; - var allLinePaths = linePaths; + var allPaths; + var defaultPath; var numRecords = records.length - 1; var x = 0; var y = 0; @@ -1391,10 +1422,17 @@ function convertRecordsToStyledPaths(records, fillPaths, linePaths, dictionary, applySegmentToStyles(segment, styles, linePaths, fillPaths, isMorph); } if (record.hasNewStyles) { + if (!allPaths) { + allPaths = []; + } + push.apply(allPaths, fillPaths); fillPaths = createPathsList(record.fillStyles, false, dictionary, dependencies); - push.apply(allFillPaths, fillPaths); + push.apply(allPaths, linePaths); linePaths = createPathsList(record.lineStyles, true, dictionary, dependencies); - push.apply(allLinePaths, linePaths); + if (defaultPath) { + allPaths.push(defaultPath); + defaultPath = null; + } styles = { fill0: 0, fill1: 0, @@ -1438,7 +1476,33 @@ function convertRecordsToStyledPaths(records, fillPaths, linePaths, dictionary, } } } else { + if (!segment) { + if (!defaultPath) { + var style = { + color: { + red: 0, + green: 0, + blue: 0, + alpha: 255 + }, + width: 20 + }; + defaultPath = new SegmentedPath(null, processStyle(style, true)); + } + segment = defaultPath.addSegment([], [], isMorph ? [] : null); + segment.commands.push(SHAPE_MOVE_TO); + segment.data.push(x, y); + if (isMorph) { + segment.morphData.push(morphX, morphY); + } + } if (isMorph) { + while (morphRecord && morphRecord.type === 0) { + morphRecord = recordsMorph[j++]; + } + if (!morphRecord) { + morphRecord = record; + } } if (record.isStraight && (!isMorph || morphRecord.isStraight)) { x += record.deltaX | 0; @@ -1488,20 +1552,28 @@ function convertRecordsToStyledPaths(records, fillPaths, linePaths, dictionary, } } applySegmentToStyles(segment, styles, linePaths, fillPaths, isMorph); - push.apply(allFillPaths, allLinePaths); + if (allPaths) { + push.apply(allPaths, fillPaths); + } else { + allPaths = fillPaths; + } + push.apply(allPaths, linePaths); + if (defaultPath) { + allPaths.push(defaultPath); + } var removeCount = 0; - for (i = 0; i < allFillPaths.length; i++) { - path = allFillPaths[i]; + for (i = 0; i < allPaths.length; i++) { + path = allPaths[i]; if (!path.head()) { removeCount++; continue; } - allFillPaths[i - removeCount] = segmentedPathToShapePath(path, isMorph); + allPaths[i - removeCount] = segmentedPathToShapePath(path, isMorph, transferables); } - allFillPaths.length -= removeCount; - return allFillPaths; + allPaths.length -= removeCount; + return allPaths; } -function segmentedPathToShapePath(path, isMorph) { +function segmentedPathToShapePath(path, isMorph, transferables) { var start = path.head(); var end = start; var finalRoot = null; @@ -1556,7 +1628,7 @@ function segmentedPathToShapePath(path, isMorph) { totalDataLength += current.data.length; current = current.next; } - var shape = new ShapePath(path.fillStyle, path.lineStyle, totalCommandsLength, totalDataLength, isMorph); + var shape = new ShapePath(path.fillStyle, path.lineStyle, totalCommandsLength, totalDataLength, isMorph, transferables); var allCommands = shape.commands; var allData = shape.data; var allMorphData = shape.morphData; @@ -1564,13 +1636,10 @@ function segmentedPathToShapePath(path, isMorph) { var dataIndex = 0; current = finalRoot; while (current) { - var offset = 0; var commands = current.commands; var data = current.data; var morphData = current.morphData; - if (data[0] === allData[dataIndex - 2] && data[1] === allData[dataIndex - 1]) { - offset = 1; - } + var offset = +(data[0] === allData[dataIndex - 2] && data[1] === allData[dataIndex - 1]); for (var i = offset; i < commands.length; i++, commandsIndex++) { allCommands[commandsIndex] = commands[i]; } @@ -1598,7 +1667,7 @@ function processStyle(style, isLineStyle, dictionary, dependencies) { if (isLineStyle) { style.lineCap = CAPS_STYLE_TYPES[style.endCapStyle | 0]; style.lineJoin = JOIN_STYLE_TYPES[style.joinStyle | 0]; - style.miterLimit = style.miterLimitFactor * 2; + style.miterLimit = (style.miterLimitFactor || 1.5) * 2; if (!style.color && style.hasFill) { var fillStyle = processStyle(style.fillStyle, false, dictionary, dependencies); style.style = fillStyle.style; @@ -1667,9 +1736,10 @@ function createPathsList(styles, isLineStyle, dictionary, dependencies) { } function defineShape(tag, dictionary) { var dependencies = []; + var transferables = []; var fillPaths = createPathsList(tag.fillStyles, false, dictionary, dependencies); var linePaths = createPathsList(tag.lineStyles, true, dictionary, dependencies); - var paths = convertRecordsToStyledPaths(tag.records, fillPaths, linePaths, dictionary, dependencies, tag.recordsMorph || null); + var paths = convertRecordsToStyledPaths(tag.records, fillPaths, linePaths, dictionary, dependencies, tag.recordsMorph || null, transferables); if (tag.bboxMorph) { var mbox = tag.bboxMorph; extendBoundsByPoint(tag.bbox, mbox.xMin, mbox.yMin); @@ -1689,9 +1759,16 @@ function defineShape(tag, dictionary) { bboxMorph: tag.bboxMorph, isMorph: tag.isMorph, paths: paths, - require: dependencies.length ? dependencies : null + require: dependencies.length ? dependencies : null, + transferables: transferables }; } +function logShape(paths, bbox) { + var output = '{"bounds":' + JSON.stringify(bbox) + ',"paths":[' + paths.map(function (path) { + return path.serialize(); + }).join() + ']}'; + console.log(output); +} function SegmentedPath(fillStyle, lineStyle) { this.fillStyle = fillStyle; this.lineStyle = lineStyle; @@ -1745,7 +1822,7 @@ var SHAPE_WIDE_LINE_TO = 5; var SHAPE_CUBIC_CURVE_TO = 6; var SHAPE_CIRCLE = 7; var SHAPE_ELLIPSE = 8; -function ShapePath(fillStyle, lineStyle, commandsCount, dataLength, isMorph) { +function ShapePath(fillStyle, lineStyle, commandsCount, dataLength, isMorph, transferables) { this.fillStyle = fillStyle; this.lineStyle = lineStyle; if (commandsCount) { @@ -1758,8 +1835,21 @@ function ShapePath(fillStyle, lineStyle, commandsCount, dataLength, isMorph) { } this.bounds = null; this.strokeBounds = null; - this.isMorph = isMorph; + this.isMorph = !(!isMorph); this.fullyInitialized = false; + if (inWorker) { + this.buffers = [ + this.commands.buffer, + this.data.buffer + ]; + transferables.push(this.commands.buffer, this.data.buffer); + if (isMorph) { + this.buffers.push(this.morphData.buffer); + transferables.push(this.morphData.buffer); + } + } else { + this.buffers = null; + } } ShapePath.prototype = { moveTo: function (x, y) { @@ -1808,8 +1898,8 @@ ShapePath.prototype = { var formOpen = false; var formOpenX = 0; var formOpenY = 0; - for (var j = 0, k = 0; j < commands.length; j++) { - if (!this.isMorph) { + if (!this.isMorph) { + for (var j = 0, k = 0; j < commands.length; j++) { switch (commands[j]) { case SHAPE_MOVE_TO: formOpen = true; @@ -1874,9 +1964,14 @@ ShapePath.prototype = { } break; default: + if (commands[j] === 0 && j === commands.length - 1) { + break; + } console.warn('Unknown drawing command encountered: ' + commands[j]); } - } else { + } + } else { + for (var j = 0, k = 0; j < commands.length; j++) { switch (commands[j]) { case SHAPE_MOVE_TO: ctx.moveTo(morph(data[k] / 20, morphData[k++] / 20, ratio), morph(data[k] / 20, morphData[k++] / 20, ratio)); @@ -2359,8 +2454,29 @@ ShapePath.prototype = { this.strokeBounds = bounds; } return bounds; + }, + serialize: function () { + var output = '{'; + if (this.fillStyle) { + output += '"fill":' + JSON.stringify(this.fillStyle) + ','; + } + if (this.lineStyle) { + output += '"stroke":' + JSON.stringify(this.lineStyle) + ','; + } + output += '"commands":[' + Array.apply([], this.commands).join() + '],'; + output += '"data":[' + Array.apply([], this.data).join() + ']'; + return output + '}'; } }; +ShapePath.fromPlainObject = function (obj) { + var path = new ShapePath(obj.fill || null, obj.stroke || null); + path.commands = new Uint8Array(obj.commands); + path.data = new Int32Array(obj.data); + if (!inWorker) { + finishShapePath(path); + } + return path; +}; function distanceSq(x1, y1, x2, y2) { var dX = x2 - x1; var dY = y2 - y1; @@ -2578,23 +2694,24 @@ function extendBoundsByY(bounds, y) { function morph(start, end, ratio) { return start + (end - start) * ratio; } -function finishShapePaths(paths, dictionary) { - for (var i = 0; i < paths.length; i++) { - var path = paths[i]; - if (path.fullyInitialized) { - continue; - } - if (!(path instanceof ShapePath)) { - var untypedPath = path; - path = paths[i] = new ShapePath(path.fillStyle, path.lineStyle, 0, 0, path.isMorph); - path.commands = untypedPath.commands; - path.data = untypedPath.data; - path.morphData = untypedPath.morphData; - } - path.fillStyle && initStyle(path.fillStyle, dictionary); - path.lineStyle && initStyle(path.lineStyle, dictionary); - path.fullyInitialized = true; +function finishShapePath(path, dictionary) { + if (path.fullyInitialized) { + return path; } + if (!(path instanceof ShapePath)) { + var untypedPath = path; + path = new ShapePath(path.fillStyle, path.lineStyle, 0, 0, path.isMorph); + path.commands = new Uint8Array(untypedPath.buffers[0]); + path.data = new Int32Array(untypedPath.buffers[1]); + if (untypedPath.isMorph) { + path.morphData = new Int32Array(untypedPath.buffers[2]); + } + path.buffers = null; + } + path.fillStyle && initStyle(path.fillStyle, dictionary); + path.lineStyle && initStyle(path.lineStyle, dictionary); + path.fullyInitialized = true; + return path; } var inWorker = typeof window === 'undefined'; var factoryCtx = !inWorker ? document.createElement('canvas').getContext('2d') : null; @@ -2610,7 +2727,7 @@ function buildLinearGradientFactory(colorStops) { } return gradient; }; - fn.defaultGradient = defaultGradient; + fn.defaultFillStyle = defaultGradient; return fn; } function buildRadialGradientFactory(focalPoint, colorStops) { @@ -2625,7 +2742,31 @@ function buildRadialGradientFactory(focalPoint, colorStops) { } return gradient; }; - fn.defaultGradient = defaultGradient; + fn.defaultFillStyle = defaultGradient; + return fn; +} +function buildBitmapPatternFactory(img, repeat) { + var defaultPattern = factoryCtx.createPattern(img, repeat); + var cachedTransform, cachedTransformKey; + var fn = function createBitmapPattern(ctx, colorTransform) { + if (!colorTransform.mode) { + return defaultPattern; + } + var key = colorTransform.getTransformFingerprint(); + if (key === cachedTransformKey) { + return cachedTransform; + } + var canvas = document.createElement('canvas'); + canvas.width = img.width; + canvas.height = img.height; + var ctx = canvas.getContext('2d'); + colorTransform.setAlpha(ctx, true); + ctx.drawImage(img, 0, 0); + cachedTransform = ctx.createPattern(canvas, repeat); + cachedTransformKey = key; + return cachedTransform; + }; + fn.defaultFillStyle = defaultPattern; return fn; } function initStyle(style, dictionary) { @@ -2662,7 +2803,7 @@ function initStyle(style, dictionary) { case GRAPHICS_FILL_NONSMOOTHED_CLIPPED_BITMAP: var bitmap = dictionary[style.bitmapId]; var repeat = style.type === GRAPHICS_FILL_REPEATING_BITMAP || style.type === GRAPHICS_FILL_NONSMOOTHED_REPEATING_BITMAP; - style.style = factoryCtx.createPattern(bitmap.value.props.img, repeat ? 'repeat' : 'no-repeat'); + style.style = buildBitmapPatternFactory(bitmap.value.props.img, repeat ? 'repeat' : 'no-repeat'); break; default: fail('invalid fill style', 'shape'); @@ -3120,1143 +3261,407 @@ function defineText(tag, dictionary) { return props; } var $RELEASE = false; -var LoaderDefinition = function () { - var WORKERS_ENABLED = true; - var LOADER_PATH; - var workerScripts; - if (true) { - LOADER_PATH = 'shumway-worker.js'; - } else { - LOADER_PATH = 'flash/display/Loader.js'; - workerScripts = [ - '../../../lib/DataView.js/DataView.js', - '../util.js', - '../../swf/config.js', - '../../swf/util.js', - '../../swf/swf.js', - '../../swf/types.js', - '../../swf/structs.js', - '../../swf/tags.js', - '../../swf/inflate.js', - '../../swf/stream.js', - '../../swf/templates.js', - '../../swf/generator.js', - '../../swf/handlers.js', - '../../swf/parser.js', - '../../swf/bitmap.js', - '../../swf/button.js', - '../../swf/font.js', - '../../swf/image.js', - '../../swf/label.js', - '../../swf/shape.js', - '../../swf/sound.js', - '../../swf/text.js' - ]; +var isWorker = typeof window === 'undefined'; +if (isWorker && !true) { + importScripts.apply(null, [ + '../../lib/DataView.js/DataView.js', + '../flash/util.js', + 'config.js', + 'swf.js', + 'types.js', + 'structs.js', + 'tags.js', + 'inflate.js', + 'stream.js', + 'templates.js', + 'generator.js', + 'handlers.js', + 'parser.js', + 'bitmap.js', + 'button.js', + 'font.js', + 'image.js', + 'label.js', + 'shape.js', + 'sound.js', + 'text.js' + ]); +} +function defineSymbol(swfTag, symbols) { + var symbol; + switch (swfTag.code) { + case SWF_TAG_CODE_DEFINE_BITS: + case SWF_TAG_CODE_DEFINE_BITS_JPEG2: + case SWF_TAG_CODE_DEFINE_BITS_JPEG3: + case SWF_TAG_CODE_DEFINE_BITS_JPEG4: + case SWF_TAG_CODE_JPEG_TABLES: + symbol = defineImage(swfTag, symbols); + break; + case SWF_TAG_CODE_DEFINE_BITS_LOSSLESS: + case SWF_TAG_CODE_DEFINE_BITS_LOSSLESS2: + symbol = defineBitmap(swfTag); + break; + case SWF_TAG_CODE_DEFINE_BUTTON: + case SWF_TAG_CODE_DEFINE_BUTTON2: + symbol = defineButton(swfTag, symbols); + break; + case SWF_TAG_CODE_DEFINE_EDIT_TEXT: + symbol = defineText(swfTag, symbols); + break; + case SWF_TAG_CODE_DEFINE_FONT: + case SWF_TAG_CODE_DEFINE_FONT2: + case SWF_TAG_CODE_DEFINE_FONT3: + case SWF_TAG_CODE_DEFINE_FONT4: + symbol = defineFont(swfTag, symbols); + break; + case SWF_TAG_CODE_DEFINE_MORPH_SHAPE: + case SWF_TAG_CODE_DEFINE_MORPH_SHAPE2: + case SWF_TAG_CODE_DEFINE_SHAPE: + case SWF_TAG_CODE_DEFINE_SHAPE2: + case SWF_TAG_CODE_DEFINE_SHAPE3: + case SWF_TAG_CODE_DEFINE_SHAPE4: + symbol = defineShape(swfTag, symbols); + break; + case SWF_TAG_CODE_DEFINE_SOUND: + symbol = defineSound(swfTag, symbols); + break; + case SWF_TAG_CODE_DEFINE_BINARY_DATA: + symbol = { + type: 'binary', + id: swfTag.id, + data: swfTag.data + }; + break; + case SWF_TAG_CODE_DEFINE_SPRITE: + var depths = {}; + var frame = { + type: 'frame' + }; + var frames = []; + var tags = swfTag.tags; + var frameScripts = null; + var frameIndex = 0; + var soundStream = null; + for (var i = 0, n = tags.length; i < n; i++) { + var tag = tags[i]; + switch (tag.code) { + case SWF_TAG_CODE_DO_ACTION: + if (!frameScripts) + frameScripts = []; + frameScripts.push(frameIndex); + frameScripts.push(tag.actionsData); + break; + case SWF_TAG_CODE_START_SOUND: + var startSounds = frame.startSounds || (frame.startSounds = []); + startSounds.push(tag); + break; + case SWF_TAG_CODE_SOUND_STREAM_HEAD: + try { + soundStream = createSoundStream(tag); + frame.soundStream = soundStream.info; + } catch (e) { + } + break; + case SWF_TAG_CODE_SOUND_STREAM_BLOCK: + if (soundStream) { + frame.soundStreamBlock = soundStream.decode(tag.data); + } + break; + case SWF_TAG_CODE_FRAME_LABEL: + frame.labelName = tag.name; + break; + case SWF_TAG_CODE_PLACE_OBJECT: + case SWF_TAG_CODE_PLACE_OBJECT2: + case SWF_TAG_CODE_PLACE_OBJECT3: + depths[tag.depth] = tag; + break; + case SWF_TAG_CODE_REMOVE_OBJECT: + case SWF_TAG_CODE_REMOVE_OBJECT2: + depths[tag.depth] = null; + break; + case SWF_TAG_CODE_SHOW_FRAME: + frameIndex += tag.repeat; + frame.repeat = tag.repeat; + frame.depths = depths; + frames.push(frame); + depths = {}; + frame = { + type: 'frame' + }; + break; + } } - var isWorker = typeof window === 'undefined'; - function loadFromWorker(loader, request, context) { - var symbols = {}; - var commitData; - if (loader) { - commitData = function (data) { - return loader._commitData(data); - }; - } else { - commitData = function (data) { - self.postMessage(data); - }; - } - function defineSymbol(swfTag) { - var symbol; - switch (swfTag.code) { - case SWF_TAG_CODE_DEFINE_BITS: - case SWF_TAG_CODE_DEFINE_BITS_JPEG2: - case SWF_TAG_CODE_DEFINE_BITS_JPEG3: - case SWF_TAG_CODE_DEFINE_BITS_JPEG4: - case SWF_TAG_CODE_JPEG_TABLES: - symbol = defineImage(swfTag, symbols); - break; - case SWF_TAG_CODE_DEFINE_BITS_LOSSLESS: - case SWF_TAG_CODE_DEFINE_BITS_LOSSLESS2: - symbol = defineBitmap(swfTag, symbols); - break; - case SWF_TAG_CODE_DEFINE_BUTTON: - case SWF_TAG_CODE_DEFINE_BUTTON2: - symbol = defineButton(swfTag, symbols); - break; - case SWF_TAG_CODE_DEFINE_EDIT_TEXT: - symbol = defineText(swfTag, symbols); - break; - case SWF_TAG_CODE_DEFINE_FONT: - case SWF_TAG_CODE_DEFINE_FONT2: - case SWF_TAG_CODE_DEFINE_FONT3: - case SWF_TAG_CODE_DEFINE_FONT4: - symbol = defineFont(swfTag, symbols); - break; - case SWF_TAG_CODE_DEFINE_MORPH_SHAPE: - case SWF_TAG_CODE_DEFINE_MORPH_SHAPE2: - case SWF_TAG_CODE_DEFINE_SHAPE: - case SWF_TAG_CODE_DEFINE_SHAPE2: - case SWF_TAG_CODE_DEFINE_SHAPE3: - case SWF_TAG_CODE_DEFINE_SHAPE4: - symbol = defineShape(swfTag, symbols); - break; - case SWF_TAG_CODE_DEFINE_SOUND: - symbol = defineSound(swfTag, symbols); - break; - case SWF_TAG_CODE_DEFINE_BINARY_DATA: - symbol = { - type: 'binary', - id: swfTag.id, - data: swfTag.data - }; - break; - case SWF_TAG_CODE_DEFINE_SPRITE: - var depths = {}; - var frame = { - type: 'frame' - }; - var frames = []; - var tags = swfTag.tags; - var frameScripts = null; - var frameIndex = 0; - var soundStream = null; - for (var i = 0, n = tags.length; i < n; i++) { - var tag = tags[i]; - switch (tag.code) { - case SWF_TAG_CODE_DO_ACTION: - if (!frameScripts) - frameScripts = []; - frameScripts.push(frameIndex); - frameScripts.push(tag.actionsData); - break; - case SWF_TAG_CODE_START_SOUND: - var startSounds = frame.startSounds || (frame.startSounds = []); - startSounds.push(tag); - break; - case SWF_TAG_CODE_SOUND_STREAM_HEAD: - try { - soundStream = createSoundStream(tag); - frame.soundStream = soundStream.info; - } catch (e) { - } - break; - case SWF_TAG_CODE_SOUND_STREAM_BLOCK: - if (soundStream) { - frame.soundStreamBlock = soundStream.decode(tag.data); - } - break; - case SWF_TAG_CODE_FRAME_LABEL: - frame.labelName = tag.name; - break; - case SWF_TAG_CODE_PLACE_OBJECT: - case SWF_TAG_CODE_PLACE_OBJECT2: - case SWF_TAG_CODE_PLACE_OBJECT3: - depths[tag.depth] = tag; - break; - case SWF_TAG_CODE_REMOVE_OBJECT: - case SWF_TAG_CODE_REMOVE_OBJECT2: - depths[tag.depth] = null; - break; - case SWF_TAG_CODE_SHOW_FRAME: - var repeat = 1; - while (i < n - 1) { - var nextTag = tags[i + 1]; - if (nextTag.code !== SWF_TAG_CODE_SHOW_FRAME) - break; - i++; - repeat++; - } - frameIndex += repeat; - frame.repeat = repeat; - frame.depths = depths; - frames.push(frame); - depths = {}; - frame = { - type: 'frame' - }; - break; - } + symbol = { + type: 'sprite', + id: swfTag.id, + frameCount: swfTag.frameCount, + frames: frames, + frameScripts: frameScripts + }; + break; + case SWF_TAG_CODE_DEFINE_TEXT: + case SWF_TAG_CODE_DEFINE_TEXT2: + symbol = defineLabel(swfTag, symbols); + break; + } + if (!symbol) { + return { + command: 'error', + message: 'unknown symbol type: ' + swfTag.code + }; + } + symbol.isSymbol = true; + symbols[swfTag.id] = symbol; + return symbol; +} +function createParsingContext(commitData) { + var depths = {}; + var symbols = {}; + var frame = { + type: 'frame' + }; + var tagsProcessed = 0; + var soundStream = null; + var lastProgressSent = 0; + return { + onstart: function (result) { + commitData({ + command: 'init', + result: result + }); + }, + onprogress: function (result) { + if (Date.now() - lastProgressSent > 1000 / 24 || result.bytesLoaded === result.bytesTotal) { + commitData({ + command: 'progress', + result: { + bytesLoaded: result.bytesLoaded, + bytesTotal: result.bytesTotal } - symbol = { - type: 'sprite', - id: swfTag.id, - frameCount: swfTag.frameCount, - frames: frames, - frameScripts: frameScripts - }; - break; - case SWF_TAG_CODE_DEFINE_TEXT: - case SWF_TAG_CODE_DEFINE_TEXT2: - symbol = defineLabel(swfTag, symbols); - break; - } - if (!symbol) { - commitData({ - command: 'error', - message: 'unknown symbol type: ' + swfTag.code - }); - return; - } - symbol.isSymbol = true; - symbols[swfTag.id] = symbol; - commitData(symbol); + }); + lastProgressSent = Date.now(); } - function createParsingContext() { - var depths = {}; - var frame = { + var tags = result.tags; + for (var n = tags.length; tagsProcessed < n; tagsProcessed++) { + var tag = tags[tagsProcessed]; + if ('id' in tag) { + var symbol = defineSymbol(tag, symbols); + commitData(symbol, symbol.transferables); + continue; + } + switch (tag.code) { + case SWF_TAG_CODE_DEFINE_SCENE_AND_FRAME_LABEL_DATA: + frame.sceneData = tag; + break; + case SWF_TAG_CODE_DEFINE_SCALING_GRID: + var symbolUpdate = { + isSymbol: true, + id: tag.symbolId, + updates: { + scale9Grid: tag.splitter + } + }; + commitData(symbolUpdate); + break; + case SWF_TAG_CODE_DO_ABC: + case SWF_TAG_CODE_DO_ABC_: + var abcBlocks = frame.abcBlocks; + if (abcBlocks) + abcBlocks.push({ + data: tag.data, + flags: tag.flags + }); + else + frame.abcBlocks = [ + { + data: tag.data, + flags: tag.flags + } + ]; + break; + case SWF_TAG_CODE_DO_ACTION: + var actionBlocks = frame.actionBlocks; + if (actionBlocks) + actionBlocks.push(tag.actionsData); + else + frame.actionBlocks = [ + tag.actionsData + ]; + break; + case SWF_TAG_CODE_DO_INIT_ACTION: + var initActionBlocks = frame.initActionBlocks || (frame.initActionBlocks = []); + initActionBlocks.push({ + spriteId: tag.spriteId, + actionsData: tag.actionsData + }); + break; + case SWF_TAG_CODE_START_SOUND: + var startSounds = frame.startSounds; + if (!startSounds) + frame.startSounds = startSounds = []; + startSounds.push(tag); + break; + case SWF_TAG_CODE_SOUND_STREAM_HEAD: + try { + soundStream = createSoundStream(tag); + frame.soundStream = soundStream.info; + } catch (e) { + } + break; + case SWF_TAG_CODE_SOUND_STREAM_BLOCK: + if (soundStream) { + frame.soundStreamBlock = soundStream.decode(tag.data); + } + break; + case SWF_TAG_CODE_EXPORT_ASSETS: + var exports = frame.exports; + if (exports) + frame.exports = exports.concat(tag.exports); + else + frame.exports = tag.exports.slice(0); + break; + case SWF_TAG_CODE_SYMBOL_CLASS: + var symbolClasses = frame.symbolClasses; + if (symbolClasses) + frame.symbolClasses = symbolClasses.concat(tag.exports); + else + frame.symbolClasses = tag.exports.slice(0); + break; + case SWF_TAG_CODE_FRAME_LABEL: + frame.labelName = tag.name; + break; + case SWF_TAG_CODE_PLACE_OBJECT: + case SWF_TAG_CODE_PLACE_OBJECT2: + case SWF_TAG_CODE_PLACE_OBJECT3: + depths[tag.depth] = tag; + break; + case SWF_TAG_CODE_REMOVE_OBJECT: + case SWF_TAG_CODE_REMOVE_OBJECT2: + depths[tag.depth] = null; + break; + case SWF_TAG_CODE_SET_BACKGROUND_COLOR: + frame.bgcolor = tag.color; + break; + case SWF_TAG_CODE_SHOW_FRAME: + frame.repeat = tag.repeat; + frame.depths = depths; + frame.complete = !(!tag.finalTag); + commitData(frame); + depths = {}; + frame = { type: 'frame' }; - var tagsProcessed = 0; - var soundStream = null; - var lastProgressSent = 0; - return { - onstart: function (result) { - commitData({ - command: 'init', - result: result - }); - }, - onprogress: function (result) { - if (Date.now() - lastProgressSent > 1000 / 24 || result.bytesLoaded === result.bytesTotal) { - commitData({ - command: 'progress', - result: { - bytesLoaded: result.bytesLoaded, - bytesTotal: result.bytesTotal - } - }); - lastProgressSent = Date.now(); - } - var tags = result.tags; - for (var n = tags.length; tagsProcessed < n; tagsProcessed++) { - var tag = tags[tagsProcessed]; - if ('id' in tag) { - defineSymbol(tag); - continue; - } - switch (tag.code) { - case SWF_TAG_CODE_DEFINE_SCENE_AND_FRAME_LABEL_DATA: - frame.sceneData = tag; - break; - case SWF_TAG_CODE_DEFINE_SCALING_GRID: - var symbolUpdate = { - isSymbol: true, - id: tag.symbolId, - updates: { - scale9Grid: tag.splitter - } - }; - commitData(symbolUpdate); - break; - case SWF_TAG_CODE_DO_ABC: - case SWF_TAG_CODE_DO_ABC_: - var abcBlocks = frame.abcBlocks; - if (abcBlocks) - abcBlocks.push({ - data: tag.data, - flags: tag.flags - }); - else - frame.abcBlocks = [ - { - data: tag.data, - flags: tag.flags - } - ]; - break; - case SWF_TAG_CODE_DO_ACTION: - var actionBlocks = frame.actionBlocks; - if (actionBlocks) - actionBlocks.push(tag.actionsData); - else - frame.actionBlocks = [ - tag.actionsData - ]; - break; - case SWF_TAG_CODE_DO_INIT_ACTION: - var initActionBlocks = frame.initActionBlocks || (frame.initActionBlocks = []); - initActionBlocks.push({ - spriteId: tag.spriteId, - actionsData: tag.actionsData - }); - break; - case SWF_TAG_CODE_START_SOUND: - var startSounds = frame.startSounds; - if (!startSounds) - frame.startSounds = startSounds = []; - startSounds.push(tag); - break; - case SWF_TAG_CODE_SOUND_STREAM_HEAD: - try { - soundStream = createSoundStream(tag); - frame.soundStream = soundStream.info; - } catch (e) { - } - break; - case SWF_TAG_CODE_SOUND_STREAM_BLOCK: - if (soundStream) { - frame.soundStreamBlock = soundStream.decode(tag.data); - } - break; - case SWF_TAG_CODE_SYMBOL_CLASS: - var exports = frame.exports; - if (exports) - frame.exports = exports.concat(tag.exports); - else - frame.exports = tag.exports.slice(0); - break; - case SWF_TAG_CODE_FRAME_LABEL: - frame.labelName = tag.name; - break; - case SWF_TAG_CODE_PLACE_OBJECT: - case SWF_TAG_CODE_PLACE_OBJECT2: - case SWF_TAG_CODE_PLACE_OBJECT3: - depths[tag.depth] = tag; - break; - case SWF_TAG_CODE_REMOVE_OBJECT: - case SWF_TAG_CODE_REMOVE_OBJECT2: - depths[tag.depth] = null; - break; - case SWF_TAG_CODE_SET_BACKGROUND_COLOR: - frame.bgcolor = tag.color; - break; - case SWF_TAG_CODE_SHOW_FRAME: - var repeat = 1; - while (tagsProcessed < n) { - var nextTag = tags[tagsProcessed + 1]; - if (!nextTag || nextTag.code !== SWF_TAG_CODE_SHOW_FRAME) - break; - tagsProcessed++; - repeat++; - } - frame.repeat = repeat; - frame.depths = depths; - commitData(frame); - depths = {}; - frame = { - type: 'frame' - }; - break; - } - } - }, - oncomplete: function (result) { - commitData(result); - var stats; - if (typeof result.swfVersion === 'number') { - var bbox = result.bbox; - stats = { - topic: 'parseInfo', - parseTime: result.parseTime, - bytesTotal: result.bytesTotal, - swfVersion: result.swfVersion, - frameRate: result.frameRate, - width: (bbox.xMax - bbox.xMin) / 20, - height: (bbox.yMax - bbox.yMin) / 20, - isAvm2: !(!result.fileAttributes.doAbc) - }; - } - commitData({ - command: 'complete', - stats: stats - }); - } - }; - } - function parseBytes(bytes) { - SWF.parse(bytes, createParsingContext()); - } - if (isWorker || !flash.net.URLRequest.class.isInstanceOf(request)) { - var input = request; - if (input instanceof ArrayBuffer) { - parseBytes(input); - } else if ('subscribe' in input) { - var pipe = SWF.parseAsync(createParsingContext()); - input.subscribe(function (data, progress) { - if (data) { - pipe.push(data, progress); - } else { - pipe.close(); - } - }); - } else if (typeof FileReaderSync !== 'undefined') { - var reader = new FileReaderSync(); - var buffer = reader.readAsArrayBuffer(input); - parseBytes(buffer); - } else { - var reader = new FileReader(); - reader.onload = function () { - parseBytes(this.result); - }; - reader.readAsArrayBuffer(input); + break; } - } else { - var session = FileLoadingService.createSession(); - var pipe = SWF.parseAsync(createParsingContext()); - session.onprogress = function (data, progressState) { - pipe.push(data, progressState); - var data = { - command: 'progress', - result: { - bytesLoaded: progressState.bytesLoaded, - bytesTotal: progressState.bytesTotal - } - }; - loader._commitData(data); - }; - session.onerror = function (error) { - loader._commitData({ - command: 'error', - error: error - }); - }; - session.onopen = function () { - }; - session.onclose = function () { - pipe.close(); - }; - session.open(request._toFileRequest()); } + }, + oncomplete: function (result) { + commitData(result); + var stats; + if (typeof result.swfVersion === 'number') { + var bbox = result.bbox; + stats = { + topic: 'parseInfo', + parseTime: result.parseTime, + bytesTotal: result.bytesTotal, + swfVersion: result.swfVersion, + frameRate: result.frameRate, + width: (bbox.xMax - bbox.xMin) / 20, + height: (bbox.yMax - bbox.yMin) / 20, + isAvm2: !(!result.fileAttributes.doAbc) + }; + } + commitData({ + command: 'complete', + stats: stats + }); } - if (isWorker) { - if (workerScripts) { - importScripts.apply(null, workerScripts); - } - var subscription = null; - self.onmessage = function (evt) { - if (subscription) { - subscription.callback(evt.data.data, evt.data.progress); - } else if (evt.data === 'pipe:') { - subscription = { - subscribe: function (callback) { - this.callback = callback; - } - }; - loadFromWorker(null, subscription); - } else { - loadFromWorker(null, evt.data); - } - }; - return; - } - var head = document.head; - head.insertBefore(document.createElement('style'), head.firstChild); - var style = document.styleSheets[0]; - var def = { - __class__: 'flash.display.Loader', - initialize: function () { - this._contentLoaderInfo = new flash.display.LoaderInfo(); - this._contentLoaderInfo._loader = this; - this._dictionary = {}; - this._displayList = null; - this._timeline = []; - this._lastPromise = null; - this._uncaughtErrorEvents = null; - this._worker = null; - }, - _commitData: function (data) { - switch (data.command) { - case 'init': - this._init(data.result); - break; - case 'progress': - this._updateProgress(data.result); - break; - case 'complete': - var frameConstructed = new Promise(); - avm2.systemDomain.onMessage.register('frameConstructed', function waitForFrame(type) { - if (type === 'frameConstructed') { - frameConstructed.resolve(); - avm2.systemDomain.onMessage.unregister('frameConstructed', waitForFrame); - } - }); - Promise.when(frameConstructed, this._lastPromise).then(function () { - this.contentLoaderInfo._dispatchEvent('complete'); - }.bind(this)); - var stats = data.stats; - if (stats) { - TelemetryService.reportTelemetry(stats); - } - this._worker && this._worker.terminate(); - break; - case 'empty': - this._lastPromise = new Promise(); - this._lastPromise.resolve(); - break; - case 'error': - this.contentLoaderInfo._dispatchEvent('ioError', flash.events.IOErrorEvent); - break; - default: - if (data.id === 0) - break; - if (data.isSymbol) - this._commitSymbol(data); - else if (data.type === 'frame') - this._commitFrame(data); - else if (data.type === 'image') - this._commitImage(data); - break; - } - }, - _updateProgress: function (state) { - var loaderInfo = this.contentLoaderInfo; - loaderInfo._bytesLoaded = state.bytesLoaded || 0; - loaderInfo._bytesTotal = state.bytesTotal || 0; - var event = new flash.events.ProgressEvent('progress', false, false, loaderInfo._bytesLoaded, loaderInfo._bytesTotal); - loaderInfo._dispatchEvent(event); - }, - _buildFrame: function (currentDisplayList, timeline, promiseQueue, frame, frameNum) { - var loader = this; - var dictionary = loader._dictionary; - var displayList = {}; - var depths = []; - var cmds = frame.depths; - if (currentDisplayList) { - var currentDepths = currentDisplayList.depths; - for (var i = 0; i < currentDepths.length; i++) { - var depth = currentDepths[i]; - if (cmds[depth] === null) { - continue; - } - displayList[depth] = currentDisplayList[depth]; - depths.push(depth); - } - } - for (var depth in cmds) { - var cmd = cmds[depth]; - if (!cmd) { - continue; - } - if (cmd.move) { - var oldCmd = cmd; - cmd = cloneObject(currentDisplayList[depth]); - for (var prop in oldCmd) { - var val = oldCmd[prop]; - if (val) { - cmd[prop] = val; - } - } - } - if (cmd.symbolId) { - var itemPromise = dictionary[cmd.symbolId]; - if (itemPromise && !itemPromise.resolved) { - promiseQueue.push(itemPromise); - } - cmd = cloneObject(cmd); - cmd.promise = itemPromise; - } - if (!displayList[depth]) { - depths.push(depth); - } - displayList[depth] = cmd; - } - depths.sort(sortNumeric); - displayList.depths = depths; - var i = frame.repeat; - while (i--) { - timeline.push(displayList); - } - return displayList; - }, - _commitFrame: function (frame) { - var abcBlocks = frame.abcBlocks; - var actionBlocks = frame.actionBlocks; - var initActionBlocks = frame.initActionBlocks; - var exports = frame.exports; - var sceneData = frame.sceneData; - var loader = this; - var dictionary = loader._dictionary; - var loaderInfo = loader.contentLoaderInfo; - var timeline = loader._timeline; - var frameNum = timeline.length + 1; - var framePromise = new Promise(); - var labelName = frame.labelName; - var prevPromise = this._lastPromise; - this._lastPromise = framePromise; - var promiseQueue = [ - prevPromise - ]; - this._displayList = this._buildFrame(this._displayList, timeline, promiseQueue, frame, frameNum); - if (frame.bgcolor) - loaderInfo._backgroundColor = frame.bgcolor; - else if (isNullOrUndefined(loaderInfo._backgroundColor)) - loaderInfo._backgroundColor = { - red: 255, - green: 255, - blue: 255, - alpha: 255 - }; - Promise.when.apply(Promise, promiseQueue).then(function () { - if (abcBlocks && loader._isAvm2Enabled) { - var appDomain = avm2.applicationDomain; - for (var i = 0, n = abcBlocks.length; i < n; i++) { - var abc = new AbcFile(abcBlocks[i].data, 'abc_block_' + i); - if (abcBlocks[i].flags) { - appDomain.loadAbc(abc); - } else { - appDomain.executeAbc(abc); - } - } - } - if (exports && loader._isAvm2Enabled) { - var exportPromises = []; - for (var i = 0, n = exports.length; i < n; i++) { - var asset = exports[i]; - var symbolPromise = dictionary[asset.symbolId]; - if (!symbolPromise) - continue; - symbolPromise.then(function (symbolPromise, className) { - return function symbolPromiseResolved() { - var symbolInfo = symbolPromise.value; - symbolInfo.className = className; - avm2.applicationDomain.getClass(className).setSymbol(symbolInfo.props); - }; - }(symbolPromise, asset.className)); - exportPromises.push(symbolPromise); - } - return Promise.when.apply(Promise, exportPromises); - } - }).then(function () { - var root = loader._content; - var labelMap; - if (!root) { - var parent = loader._parent; - true; - var rootInfo = dictionary[0].value; - var rootClass = avm2.applicationDomain.getClass(rootInfo.className); - root = rootClass.createAsSymbol({ - framesLoaded: timeline.length, - loader: loader, - parent: parent || loader, - index: parent ? 0 : -1, - level: parent ? 0 : -1, - timeline: timeline, - totalFrames: rootInfo.props.totalFrames, - stage: loader._stage - }); - var isRootMovie = parent && parent == loader._stage && loader._stage._children.length === 0; - if (isRootMovie) { - parent._frameRate = loaderInfo._frameRate; - parent._stageHeight = loaderInfo._height; - parent._stageWidth = loaderInfo._width; - parent._root = root; - parent._setup(); - } else { - loader._children.push(root); - } - var labels; - labelMap = root.symbol.labelMap = createEmptyObject(); - if (sceneData) { - var scenes = []; - var startFrame; - var endFrame = root.symbol.totalFrames - 1; - var sd = sceneData.scenes; - var ld = sceneData.labels; - var i = sd.length; - while (i--) { - var s = sd[i]; - startFrame = s.offset; - labels = []; - var j = ld.length; - while (j--) { - var lbl = ld[j]; - if (lbl.frame >= startFrame && lbl.frame <= endFrame) { - labelMap[lbl.name] = lbl.frame + 1; - labels.unshift(new flash.display.FrameLabel(lbl.name, lbl.frame - startFrame + 1)); - } - } - var scene = new flash.display.Scene(s.name, labels, endFrame - startFrame + 1); - scene._startFrame = startFrame + 1; - scene._endFrame = endFrame + 1; - scenes.unshift(scene); - endFrame = startFrame - 1; - } - root.symbol.scenes = scenes; - } else { - labels = []; - if (labelName) { - labelMap[labelName] = frameNum; - labels.push(new flash.display.FrameLabel(labelName, frameNum)); - } - var scene = new flash.display.Scene('Scene 1', labels, root.symbol.totalFrames); - scene._endFrame = root.symbol.totalFrames; - root.symbol.scenes = [ - scene - ]; - } - if (!loader._isAvm2Enabled) { - var avm1Context = loader._avm1Context; - var _root = root; - if (parent && parent !== loader._stage) { - var parentLoader = parent._loader; - while (parentLoader._parent && parentLoader._parent !== loader._stage) { - parentLoader = parentLoader._parent._loader; - } - _root = parentLoader._content; - } - var as2Object = _root._getAS2Object(); - avm1Context.globals.asSetPublicProperty('_root', as2Object); - avm1Context.globals.asSetPublicProperty('_level0', as2Object); - avm1Context.globals.asSetPublicProperty('_level1', as2Object); - var parameters = loader.loaderInfo._parameters; - for (var paramName in parameters) { - if (!(paramName in as2Object)) { - as2Object[paramName] = parameters[paramName]; - } - } - } - rootClass.instanceConstructor.call(root); - loader._content = root; - } else { - root._framesLoaded += frame.repeat; - if (labelName && root._labelMap) { - if (root._labelMap[labelName] === undefined) { - root._labelMap[labelName] = frameNum; - for (var i = 0, n = root.symbol.scenes.length; i < n; i++) { - var scene = root.symbol.scenes[i]; - if (frameNum >= scene._startFrame && frameNum <= scene._endFrame) { - scene.labels.push(new flash.display.FrameLabel(labelName, frameNum - scene._startFrame)); - break; - } - } - } - } - } - if (frame.startSounds) { - root._registerStartSounds(frameNum, frame.startSounds); - } - if (frame.soundStream) { - root._initSoundStream(frame.soundStream); - } - if (frame.soundStreamBlock) { - root._addSoundStreamBlock(frameNum, frame.soundStreamBlock); - } - if (!loader._isAvm2Enabled) { - var avm1Context = loader._avm1Context; - if (initActionBlocks) { - for (var i = 0; i < initActionBlocks.length; i++) { - var spriteId = initActionBlocks[i].spriteId; - var actionsData = initActionBlocks[i].actionsData; - root.addFrameScript(frameNum - 1, function (actionsData, spriteId, state) { - if (state.executed) - return; - state.executed = true; - return executeActions(actionsData, avm1Context, this._getAS2Object(), exports); - }.bind(root, actionsData, spriteId, { - executed: false - })); - } - } - if (actionBlocks) { - for (var i = 0; i < actionBlocks.length; i++) { - var block = actionBlocks[i]; - root.addFrameScript(frameNum - 1, function (block) { - return function () { - return executeActions(block, avm1Context, this._getAS2Object(), exports); - }; - }(block)); - } - } - } - if (frameNum === 1) - loaderInfo._dispatchEvent(new flash.events.Event('init', false, false)); - framePromise.resolve(frame); - }); - }, - _commitImage: function (imageInfo) { - var loader = this; - var imgPromise = this._lastPromise = new Promise(); - var img = new Image(); - imageInfo.props.img = img; - img.onload = function () { - var props = imageInfo.props; - props.parent = loader._parent; - props.stage = loader._stage; - props.skipCopyToCanvas = true; - var Bitmap = avm2.systemDomain.getClass('flash.display.Bitmap'); - var BitmapData = avm2.systemDomain.getClass('flash.display.BitmapData'); - var bitmapData = BitmapData.createAsSymbol(props); - BitmapData.instanceConstructor.call(bitmapData, 0, 0, true, 4294967295); - var image = Bitmap.createAsSymbol(bitmapData); - loader._children.push(image); - Bitmap.instanceConstructor.call(image, bitmapData); - image._parent = loader; - loader._content = image; - imgPromise.resolve(imageInfo); - loader.contentLoaderInfo._dispatchEvent('init'); - }; - img.src = URL.createObjectURL(imageInfo.data); - delete imageInfo.data; - }, - _commitSymbol: function (symbol) { - var dictionary = this._dictionary; - if ('updates' in symbol) { - dictionary[symbol.id].then(function (s) { - for (var i in symbol.updates) { - s.props[i] = symbol.updates[i]; - } - }); - return; - } - var className = 'flash.display.DisplayObject'; - var dependencies = symbol.require; - var promiseQueue = []; - var props = { - symbolId: symbol.id, - loader: this - }; - var symbolPromise = new Promise(); - if (dependencies && dependencies.length) { - for (var i = 0, n = dependencies.length; i < n; i++) { - var dependencyId = dependencies[i]; - var dependencyPromise = dictionary[dependencyId]; - if (dependencyPromise && !dependencyPromise.resolved) - promiseQueue.push(dependencyPromise); - } - } - switch (symbol.type) { - case 'button': - var states = {}; - for (var stateName in symbol.states) { - var characters = []; - var displayList = {}; - var state = symbol.states[stateName]; - var depths = Object.keys(state); - for (var i = 0; i < depths.length; i++) { - var depth = depths[i]; - var cmd = state[depth]; - var characterPromise = dictionary[cmd.symbolId]; - if (characterPromise && !characterPromise.resolved) - promiseQueue.push(characterPromise); - characters.push(characterPromise); - displayList[depth] = Object.create(cmd, { - promise: { - value: characterPromise - } - }); - } - depths.sort(sortNumeric); - displayList.depths = depths; - var statePromise = new Promise(); - statePromise.resolve({ - className: 'flash.display.Sprite', - props: { - loader: this, - timeline: [ - displayList - ] - } - }); - states[stateName] = statePromise; - } - className = 'flash.display.SimpleButton'; - props.states = states; - props.buttonActions = symbol.buttonActions; - break; - case 'font': - var charset = fromCharCode.apply(null, symbol.codes); - if (charset) { - style.insertRule('@font-face{font-family:"' + symbol.uniqueName + '";' + 'src:url(data:font/opentype;base64,' + btoa(symbol.data) + ')' + '}', style.cssRules.length); - if (!/Mozilla\/5.0.*?rv:(\d+).*? Gecko/.test(window.navigator.userAgent)) { - var testDiv = document.createElement('div'); - testDiv.setAttribute('style', 'position: absolute; top: 0; right: 0;visibility: hidden; z-index: -500;font-family:"' + symbol.uniqueName + '";'); - testDiv.textContent = 'font test'; - document.body.appendChild(testDiv); - var fontPromise = new Promise(); - setTimeout(function () { - fontPromise.resolve(); - document.body.removeChild(testDiv); - }, 200); - promiseQueue.push(fontPromise); - } - } - className = 'flash.text.Font'; - props.name = symbol.name; - props.uniqueName = symbol.uniqueName; - props.charset = symbol.charset; - props.bold = symbol.bold; - props.italic = symbol.italic; - props.metrics = symbol.metrics; - this._registerFont(className, props); - break; - case 'image': - var img = new Image(); - var imgPromise = new Promise(); - img.onload = function () { - if (symbol.mask) { - var maskCanvas = document.createElement('canvas'); - maskCanvas.width = symbol.width; - maskCanvas.height = symbol.height; - var maskContext = maskCanvas.getContext('2d'); - maskContext.drawImage(img, 0, 0); - var maskImageData = maskContext.getImageData(0, 0, symbol.width, symbol.height); - var maskImageDataBytes = maskImageData.data; - var symbolMaskBytes = symbol.mask; - var length = maskImageData.width * maskImageData.height; - for (var i = 0, j = 3; i < length; i++, j += 4) { - maskImageDataBytes[j] = symbolMaskBytes[i]; - } - maskContext.putImageData(maskImageData, 0, 0); - props.img = maskCanvas; - } - imgPromise.resolve(); - }; - img.src = URL.createObjectURL(symbol.data); - promiseQueue.push(imgPromise); - className = 'flash.display.Bitmap'; - props.img = img; - props.width = symbol.width; - props.height = symbol.height; - break; - case 'label': - var drawFn = new Function('c,r,ct', symbol.data); - className = 'flash.text.StaticText'; - props.bbox = symbol.bbox; - props.draw = drawFn; - break; - case 'text': - props.bbox = symbol.bbox; - props.html = symbol.html; - if (symbol.type === 'label') { - className = 'flash.text.StaticText'; - } else { - className = 'flash.text.TextField'; - props.tag = symbol.tag; - props.variableName = symbol.variableName; - } - break; - case 'shape': - className = symbol.morph ? 'flash.display.MorphShape' : 'flash.display.Shape'; - props.bbox = symbol.bbox; - props.strokeBbox = symbol.strokeBbox; - props.paths = symbol.paths; - props.dictionary = dictionary; - break; - case 'sound': - if (!symbol.pcm && !PLAY_USING_AUDIO_TAG) { - var decodePromise = new Promise(); - MP3DecoderSession.processAll(symbol.packaged.data, function (props, pcm, id3tags, error) { - props.pcm = pcm || new Uint8Array(0); - decodePromise.resolve(); - if (error) { - console.error('ERROR: ' + error); - } - }.bind(null, props)); - promiseQueue.push(decodePromise); - } - className = 'flash.media.Sound'; - props.sampleRate = symbol.sampleRate; - props.channels = symbol.channels; - props.pcm = symbol.pcm; - props.packaged = symbol.packaged; - break; - case 'binary': - props.data = symbol.data; - break; - case 'sprite': - var displayList = null; - var frameCount = symbol.frameCount; - var labelMap = {}; - var frameNum = 1; - var frames = symbol.frames; - var timeline = []; - var startSoundRegistrations = []; - for (var i = 0, n = frames.length; i < n; i++) { - var frame = frames[i]; - var frameNum = timeline.length + 1; - if (frame.labelName) { - labelMap[frame.labelName] = frameNum; - } - if (frame.startSounds) { - startSoundRegistrations[frameNum] = frame.startSounds; - for (var j = 0; j < frame.startSounds.length; j++) { - var itemPromise = dictionary[frame.startSounds[j].soundId]; - if (itemPromise && !itemPromise.resolved) - promiseQueue.push(itemPromise); - } - } - displayList = this._buildFrame(displayList, timeline, promiseQueue, frame, frameNum); - } - var frameScripts = {}; - if (!this._isAvm2Enabled) { - if (symbol.frameScripts) { - var data = symbol.frameScripts; - for (var i = 0; i < data.length; i += 2) { - var frameNum = data[i] + 1; - var block = data[i + 1]; - var script = function (block, loader) { - return function () { - var avm1Context = loader._avm1Context; - return executeActions(block, avm1Context, this._getAS2Object()); - }; - }(block, this); - if (!frameScripts[frameNum]) - frameScripts[frameNum] = [ - script - ]; - else - frameScripts[frameNum].push(script); - } - } - } - className = 'flash.display.MovieClip'; - props.timeline = timeline; - props.framesLoaded = frameCount; - props.labelMap = labelMap; - props.frameScripts = frameScripts; - props.totalFrames = frameCount; - props.startSoundRegistrations = startSoundRegistrations; - break; - } - dictionary[symbol.id] = symbolPromise; - Promise.when.apply(Promise, promiseQueue).then(function () { - symbolPromise.resolve({ - className: className, - props: props - }); - }); - }, - _registerFont: function (className, props) { - this._vmPromise.then(function () { - var fontClass = avm2.applicationDomain.getClass(className); - var font = fontClass.createAsSymbol(props); - fontClass.instanceConstructor.call(font); - }); - }, - _init: function (info) { - var loader = this; - var loaderInfo = loader.contentLoaderInfo; - loaderInfo._swfVersion = info.swfVersion; - var bbox = info.bbox; - loaderInfo._width = bbox.xMax - bbox.xMin; - loaderInfo._height = bbox.yMax - bbox.yMin; - loaderInfo._frameRate = info.frameRate; - var documentPromise = new Promise(); - var vmPromise = new Promise(); - vmPromise.then(function () { - documentPromise.resolve({ - className: 'flash.display.MovieClip', - props: { - totalFrames: info.frameCount - } - }); - }); - loader._dictionary[0] = documentPromise; - loader._lastPromise = documentPromise; - loader._vmPromise = vmPromise; - loader._isAvm2Enabled = info.fileAttributes.doAbc; - this._setup(); - }, - _load: function (request, checkPolicyFile, applicationDomain, securityDomain, deblockingFilter) { - if (!isWorker && flash.net.URLRequest.class.isInstanceOf(request)) { - this._contentLoaderInfo._url = request._url; - } - if (!isWorker && WORKERS_ENABLED) { - var loader = this; - var worker = loader._worker = new Worker(SHUMWAY_ROOT + LOADER_PATH); - worker.onmessage = function (evt) { - loader._commitData(evt.data); - }; - if (flash.net.URLRequest.class.isInstanceOf(request)) { - var session = FileLoadingService.createSession(); - session.onprogress = function (data, progress) { - worker.postMessage({ - data: data, - progress: progress - }); - }; - session.onerror = function (error) { - loader._commitData({ - command: 'error', - error: error - }); - }; - session.onopen = function () { - worker.postMessage('pipe:'); - }; - session.onclose = function () { - worker.postMessage({ - data: null - }); - }; - session.open(request._toFileRequest()); - } else { - worker.postMessage(request); - } - } else { - loadFromWorker(this, request); - } - }, - _setup: function () { - var loader = this; - var stage = loader._stage; - if (loader._isAvm2Enabled) { - var mouseClass = avm2.systemDomain.getClass('flash.ui.Mouse'); - mouseClass._stage = stage; - loader._vmPromise.resolve(); - return; - } - if (!avm2.loadAVM1) { - loader._vmPromise.reject('AVM1 loader is not found'); - return; - } - var loaded = function () { - var loaderInfo = loader.contentLoaderInfo; - var avm1Context = new AS2Context(loaderInfo._swfVersion); - avm1Context.stage = stage; - loader._avm1Context = avm1Context; - avm1lib.AS2Key.class.$bind(stage); - avm1lib.AS2Mouse.class.$bind(stage); - stage._addEventListener('frameConstructed', avm1Context.flushPendingScripts.bind(avm1Context), false, Number.MAX_VALUE); - loader._vmPromise.resolve(); - }; - if (avm2.isAVM1Loaded) { - loaded(); - } else { - avm2.isAVM1Loaded = true; - avm2.loadAVM1(loaded); - } - }, - get contentLoaderInfo() { - return this._contentLoaderInfo; - }, - get content() { - somewhatImplemented('Loader.content'); - return this._content; - } - }; - def.__glue__ = { - native: { - instance: { - content: Object.getOwnPropertyDescriptor(def, 'content'), - contentLoaderInfo: Object.getOwnPropertyDescriptor(def, 'contentLoaderInfo'), - _getJPEGLoaderContextdeblockingfilter: function (context) { - return 0; - }, - _load: def._load, - _loadBytes: function _loadBytes(bytes, checkPolicyFile, applicationDomain, securityDomain, requestedContentParent, parameters, deblockingFilter, allowLoadBytesCodeExecution, imageDecodingPolicy) { - def._load(bytes.a); - }, - _unload: function _unload(halt, gc) { - somewhatImplemented('Loader._unload, do we even need to do anything here?'); - }, - _close: function _close() { - somewhatImplemented('Loader._close'); - }, - _getUncaughtErrorEvents: function _getUncaughtErrorEvents() { - somewhatImplemented('Loader._getUncaughtErrorEvents'); - return this._uncaughtErrorEvents; - }, - _setUncaughtErrorEvents: function _setUncaughtErrorEvents(value) { - somewhatImplemented('Loader._setUncaughtErrorEvents'); - this._uncaughtErrorEvents = value; - } - } + }; +} +function parseBytes(bytes, commitData) { + SWF.parse(bytes, createParsingContext(commitData)); +} +function ResourceLoader(scope) { + this.subscription = null; + var self = this; + if (!isWorker) { + this.messenger = { + postMessage: function (data) { + self.onmessage({ + data: data + }); } }; - return def; - }.call(this); + } else { + this.messenger = scope; + scope.onmessage = function (event) { + self.listener(event.data); + }; + } +} +ResourceLoader.prototype = { + terminate: function () { + this.messenger = null; + this.listener = null; + }, + onmessage: function (event) { + this.listener(event.data); + }, + postMessage: function (data) { + this.listener && this.listener(data); + }, + listener: function (data) { + if (this.subscription) { + this.subscription.callback(data.data, data.progress); + } else if (data === 'pipe:') { + this.subscription = { + subscribe: function (callback) { + this.callback = callback; + } + }; + this.parseLoadedData(this.messenger, this.subscription); + } else { + this.parseLoadedData(this.messenger, data); + } + }, + parseLoadedData: function (loader, request, context) { + function commitData(data, transferables) { + try { + loader.postMessage(data, transferables); + } catch (ex) { + if (ex != 'DataCloneError') { + throw ex; + } + loader.postMessage(data); + } + } + if (request instanceof ArrayBuffer) { + parseBytes(request, commitData); + } else if ('subscribe' in request) { + var pipe = SWF.parseAsync(createParsingContext(commitData)); + request.subscribe(function (data, progress) { + if (data) { + pipe.push(data, progress); + } else { + pipe.close(); + } + }); + } else if (typeof FileReaderSync !== 'undefined') { + var reader = new FileReaderSync(); + var buffer = reader.readAsArrayBuffer(request); + parseBytes(buffer, commitData); + } else { + var reader = new FileReader(); + reader.onload = function () { + parseBytes(this.result, commitData); + }; + reader.readAsArrayBuffer(request); + } + } +}; +if (isWorker) { + var loader = new ResourceLoader(this); +} var codeLengthOrder = [ 16, 17, @@ -5226,6 +4631,19 @@ var tagHandler = function (global) { $.data = readBinary($bytes, $stream, 0); return $; } + function exportAssets($bytes, $stream, $, swfVersion, tagCode) { + $ || ($ = {}); + var exportsCount = readUi16($bytes, $stream); + var $0 = $.exports = []; + var $1 = exportsCount; + while ($1--) { + var $2 = {}; + $2.symbolId = readUi16($bytes, $stream); + $2.className = readString($bytes, $stream, 0); + $0.push($2); + } + return $; + } function symbolClass($bytes, $stream, $, swfVersion, tagCode) { $ || ($ = {}); var symbolCount = readUi16($bytes, $stream); @@ -6041,7 +5459,7 @@ var tagHandler = function (global) { 45: soundStreamHead, 46: defineShape, 48: defineFont2, - 56: undefined, + 56: exportAssets, 57: undefined, 58: undefined, 59: doAction, @@ -6095,58 +5513,79 @@ var readHeader = function readHeader($bytes, $stream, $, swfVersion, tagCode) { global['tagHandler'] = tagHandler; global['readHeader'] = readHeader; }(this)); -function readTags(context, stream, swfVersion, onprogress) { +function readTags(context, stream, swfVersion, final, onprogress) { var tags = context.tags; var bytes = stream.bytes; var lastSuccessfulPosition; + var tag = null; + if (context._readTag) { + tag = context._readTag; + delete context._readTag; + } try { - do { + while (stream.pos < stream.end) { lastSuccessfulPosition = stream.pos; stream.ensure(2); var tagCodeAndLength = readUi16(bytes, stream); + if (!tagCodeAndLength) { + final = true; + break; + } var tagCode = tagCodeAndLength >> 6; var length = tagCodeAndLength & 63; if (length === 63) { stream.ensure(4); length = readUi32(bytes, stream); } - stream.ensure(length); - if (tagCode === 0) { - break; + if (tag) { + if (tagCode === 1 && tag.code === 1) { + tag.repeat++; + stream.pos += length; + continue; + } + tags.push(tag); + if (onprogress && tag.id !== undefined) { + onprogress(context); + } + tag = null; } + stream.ensure(length); var substream = stream.substream(stream.pos, stream.pos += length); var subbytes = substream.bytes; - var tag = { + var nextTag = { code: tagCode }; if (tagCode === 39) { - tag.type = 'sprite'; - tag.id = readUi16(subbytes, substream); - tag.frameCount = readUi16(subbytes, substream); - tag.tags = []; - readTags(tag, substream, swfVersion); + nextTag.type = 'sprite'; + nextTag.id = readUi16(subbytes, substream); + nextTag.frameCount = readUi16(subbytes, substream); + nextTag.tags = []; + readTags(nextTag, substream, swfVersion, true); + } else if (tagCode === 1) { + nextTag.repeat = 1; } else { var handler = tagHandler[tagCode]; if (handler) { - handler(subbytes, substream, tag, swfVersion, tagCode); + handler(subbytes, substream, nextTag, swfVersion, tagCode); } } + tag = nextTag; + } + if (tag && final) { + tag.finalTag = true; tags.push(tag); - if (tagCode === 1) { - while (stream.pos + 2 <= stream.end && stream.getUint16(stream.pos, true) >> 6 === 1) { - tags.push(tag); - stream.pos += 2; - } - if (onprogress) - onprogress(context); - } else if (onprogress && tag.id !== undefined) { + if (onprogress) { onprogress(context); } - } while (stream.pos < stream.end); + } else { + context._readTag = tag; + } } catch (e) { - if (e !== StreamNoDataError) + if (e !== StreamNoDataError) { throw e; + } stream.pos = lastSuccessfulPosition; + context._readTag = tag; } } function HeadTailBuffer(defaultSize) { @@ -6295,12 +5734,14 @@ BodyParser.prototype = { buffer.push(data); stream = buffer.createStream(); } + var finalBlock = false; if (progressInfo) { swf.bytesLoaded = progressInfo.bytesLoaded; swf.bytesTotal = progressInfo.bytesTotal; + finalBlock = progressInfo.bytesLoaded >= progressInfo.bytesTotal; } var readStartTime = performance.now(); - readTags(swf, stream, swfVersion, options.onprogress); + readTags(swf, stream, swfVersion, finalBlock, options.onprogress); swf.parseTime += performance.now() - readStartTime; var read = stream.pos; buffer.removeHead(read); diff --git a/browser/extensions/shumway/content/shumway.js b/browser/extensions/shumway/content/shumway.js index ff7281e7f745..0381bb3ca8e9 100644 --- a/browser/extensions/shumway/content/shumway.js +++ b/browser/extensions/shumway/content/shumway.js @@ -244,6 +244,9 @@ } else if (argString.slice(0, 1) == '-' || argString.slice(0, 2) == '--') { argument = nonPositionalArgumentMap[argString]; true; + if (!argument) { + continue; + } if (argument.type !== 'boolean') { value = args.shift(); true; @@ -325,6 +328,44 @@ function assert(cond, msg, context) { if (!cond) fail(msg, context); } +function scriptProperties(namespace, props) { + return props.reduce(function (o, p) { + o[p] = namespace + ' ' + p; + return o; + }, {}); +} +function cloneObject(obj) { + var clone = Object.create(null); + for (var prop in obj) + clone[prop] = obj[prop]; + return clone; +} +function sortByDepth(a, b) { + var levelA = a._level; + var levelB = b._level; + if (a._parent !== b._parent && a._index > -1 && b._index > -1) { + while (a._level > levelB) { + a = a._parent; + } + while (b._level > levelA) { + b = b._parent; + } + while (a._level > 1) { + if (a._parent === b._parent) { + break; + } + a = a._parent; + b = b._parent; + } + } + if (a === b) { + return levelA - levelB; + } + return a._index - b._index; +} +function sortNumeric(a, b) { + return a - b; +} function rgbaObjToStr(color) { return 'rgba(' + color.red + ',' + color.green + ',' + color.blue + ',' + color.alpha / 255 + ')'; } @@ -364,14 +405,301 @@ function argbUintToStr(argb) { }); }()); var randomStyleCache; +var nextStyle = 0; function randomStyle() { if (!randomStyleCache) { - randomStyleCache = []; - for (var i = 0; i < 50; i++) { - randomStyleCache.push('#' + ('00000' + (Math.random() * (1 << 24) | 0).toString(16)).slice(-6)); + randomStyleCache = [ + '#ff5e3a', + '#ff9500', + '#ffdb4c', + '#87fc70', + '#52edc7', + '#1ad6fd', + '#c644fc', + '#ef4db6', + '#4a4a4a', + '#dbddde', + '#ff3b30', + '#ff9500', + '#ffcc00', + '#4cd964', + '#34aadc', + '#007aff', + '#5856d6', + '#ff2d55', + '#8e8e93', + '#c7c7cc', + '#5ad427', + '#c86edf', + '#d1eefc', + '#e0f8d8', + '#fb2b69', + '#f7f7f7', + '#1d77ef', + '#d6cec3', + '#55efcb', + '#ff4981', + '#ffd3e0', + '#f7f7f7', + '#ff1300', + '#1f1f21', + '#bdbec2', + '#ff3a2d' + ]; + } + return randomStyleCache[nextStyle++ % randomStyleCache.length]; +} +var Promise = function PromiseClosure() { + function isPromise(obj) { + return typeof obj === 'object' && obj !== null && typeof obj.then === 'function'; + } + function defaultOnFulfilled(value) { + return value; + } + function defaultOnRejected(reason) { + throw reason; + } + function propagateFulfilled(subject, value) { + subject.subpromisesValue = value; + var subpromises = subject.subpromises; + if (!subpromises) { + return; + } + for (var i = 0; i < subpromises.length; i++) { + subpromises[i].fulfill(value); + } + delete subject.subpromises; + } + function propagateRejected(subject, reason) { + subject.subpromisesReason = reason; + var subpromises = subject.subpromises; + if (!subpromises) { + if (!true) { + console.warn(reason); + } + return; + } + for (var i = 0; i < subpromises.length; i++) { + subpromises[i].reject(reason); + } + delete subject.subpromises; + } + function performCall(callback, arg, subject) { + try { + var value = callback(arg); + if (isPromise(value)) { + value.then(function Promise_queueCall_onFulfilled(value) { + propagateFulfilled(subject, value); + }, function Promise_queueCall_onRejected(reason) { + propagateRejected(subject, reason); + }); + return; + } + propagateFulfilled(subject, value); + } catch (ex) { + propagateRejected(subject, ex); + } + } + var queue = []; + function processQueue() { + while (queue.length > 0) { + var task = queue[0]; + if (task.directCallback) { + task.callback.call(task.subject, task.arg); + } else { + performCall(task.callback, task.arg, task.subject); + } + queue.shift(); + } + } + function queueCall(callback, arg, subject, directCallback) { + if (queue.length === 0) { + setTimeout(processQueue, 0); + } + queue.push({ + callback: callback, + arg: arg, + subject: subject, + directCallback: directCallback + }); + } + function Promise(onFulfilled, onRejected) { + this.state = 'pending'; + this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : defaultOnFulfilled; + this.onRejected = typeof onRejected === 'function' ? onRejected : defaultOnRejected; + } + Promise.prototype = { + fulfill: function Promise_resolve(value) { + if (this.state !== 'pending') { + return; + } + this.state = 'fulfilled'; + this.value = value; + queueCall(this.onFulfilled, value, this, false); + }, + reject: function Promise_reject(reason) { + if (this.state !== 'pending') { + return; + } + this.state = 'rejected'; + this.reason = reason; + queueCall(this.onRejected, reason, this, false); + }, + then: function Promise_then(onFulfilled, onRejected) { + var promise = new Promise(onFulfilled, onRejected); + if ('subpromisesValue' in this) { + queueCall(promise.fulfill, this.subpromisesValue, promise, true); + } else if ('subpromisesReason' in this) { + queueCall(promise.reject, this.subpromisesReason, promise, true); + } else { + var subpromises = this.subpromises || (this.subpromises = []); + subpromises.push(promise); + } + return promise; + }, + get resolved() { + return this.state === 'fulfilled'; + }, + resolve: function (value) { + this.fulfill(value); + } + }; + Promise.when = function Promise_when() { + var promise = new Promise(); + if (arguments.length === 0) { + promise.resolve(); + return promise; + } + var promises = slice.call(arguments, 0); + var result = []; + var i = 1; + function fulfill(value) { + result.push(value); + if (i < promises.length) { + promises[i++].then(fulfill, reject); + } else { + promise.resolve(result); + } + return value; + } + function reject(reason) { + promise.reject(reason); + } + promises[0].then(fulfill, reject); + return promise; + }; + return Promise; + }(); +var QuadTree = function (x, y, width, height, level) { + this.x = x | 0; + this.y = y | 0; + this.width = width | 0; + this.height = height | 0; + this.level = level | 0; + this.stuckObjects = []; + this.objects = []; + this.nodes = []; +}; +QuadTree.prototype._findIndex = function (xMin, yMin, xMax, yMax) { + var midX = this.x + (this.width / 2 | 0); + var midY = this.y + (this.height / 2 | 0); + var top = yMin < midY && yMax < midY; + var bottom = yMin > midY; + if (xMin < midX && xMax < midX) { + if (top) { + return 1; + } else if (bottom) { + return 2; + } + } else if (xMin > midX) { + if (top) { + return 0; + } else if (bottom) { + return 3; } } - return randomStyleCache[Math.random() * randomStyleCache.length | 0]; + return -1; +}; +QuadTree.prototype.insert = function (obj) { + var nodes = this.nodes; + if (nodes.length) { + var index = this._findIndex(obj.xMin, obj.yMin, obj.xMax, obj.yMax); + if (index > -1) { + nodes[index].insert(obj); + } else { + this.stuckObjects.push(obj); + obj._qtree = this; + } + return; + } + var objects = this.objects; + objects.push(obj); + if (objects.length > 4 && this.level < 10) { + this._subdivide(); + while (objects.length) { + this.insert(objects.shift()); + } + return; + } + obj._qtree = this; +}; +QuadTree.prototype.delete = function (obj) { + if (obj._qtree !== this) { + return; + } + var index = this.objects.indexOf(obj); + if (index > -1) { + this.objects.splice(index, 1); + } else { + index = this.stuckObjects.indexOf(obj); + this.stuckObjects.splice(index, 1); + } + obj._qtree = null; +}; +QuadTree.prototype._stack = []; +QuadTree.prototype._out = []; +QuadTree.prototype.retrieve = function (xMin, yMin, xMax, yMax) { + var stack = this._stack; + var out = this._out; + out.length = 0; + var node = this; + do { + if (node.nodes.length) { + var index = node._findIndex(xMin, yMin, xMax, yMax); + if (index > -1) { + stack.push(node.nodes[index]); + } else { + stack.push.apply(stack, node.nodes); + } + } + out.push.apply(out, node.stuckObjects); + out.push.apply(out, node.objects); + node = stack.pop(); + } while (node); + return out; +}; +QuadTree.prototype._subdivide = function () { + var halfWidth = this.width / 2 | 0; + var halfHeight = this.height / 2 | 0; + var midX = this.x + halfWidth; + var midY = this.y + halfHeight; + var level = this.level + 1; + this.nodes[0] = new QuadTree(midX, this.y, halfWidth, halfHeight, level); + this.nodes[1] = new QuadTree(this.x, this.y, halfWidth, halfHeight, level); + this.nodes[2] = new QuadTree(this.x, midY, halfWidth, halfHeight, level); + this.nodes[3] = new QuadTree(midX, midY, halfWidth, halfHeight, level); +}; +var EXTERNAL_INTERFACE_FEATURE = 1; +var CLIPBOARD_FEATURE = 2; +var SHAREDOBJECT_FEATURE = 3; +var VIDEO_FEATURE = 4; +var SOUND_FEATURE = 5; +var NETCONNECTION_FEATURE = 6; +if (!this.performance) { + this.performance = {}; +} +if (!this.performance.now) { + this.performance.now = Date.now; } var SWF_TAG_CODE_CSM_TEXT_SETTINGS = 74; var SWF_TAG_CODE_DEFINE_BINARY_DATA = 87; @@ -1517,7 +1845,7 @@ function applySegmentToStyles(segment, styles, linePaths, fillPaths, isMorph) { path.addSegment(commands, data, morphData); } } -function convertRecordsToStyledPaths(records, fillPaths, linePaths, dictionary, dependencies, recordsMorph) { +function convertRecordsToStyledPaths(records, fillPaths, linePaths, dictionary, dependencies, recordsMorph, transferables) { var isMorph = recordsMorph !== null; var styles = { fill0: 0, @@ -1525,8 +1853,8 @@ function convertRecordsToStyledPaths(records, fillPaths, linePaths, dictionary, line: 0 }; var segment = null; - var allFillPaths = fillPaths; - var allLinePaths = linePaths; + var allPaths; + var defaultPath; var numRecords = records.length - 1; var x = 0; var y = 0; @@ -1544,10 +1872,17 @@ function convertRecordsToStyledPaths(records, fillPaths, linePaths, dictionary, applySegmentToStyles(segment, styles, linePaths, fillPaths, isMorph); } if (record.hasNewStyles) { + if (!allPaths) { + allPaths = []; + } + push.apply(allPaths, fillPaths); fillPaths = createPathsList(record.fillStyles, false, dictionary, dependencies); - push.apply(allFillPaths, fillPaths); + push.apply(allPaths, linePaths); linePaths = createPathsList(record.lineStyles, true, dictionary, dependencies); - push.apply(allLinePaths, linePaths); + if (defaultPath) { + allPaths.push(defaultPath); + defaultPath = null; + } styles = { fill0: 0, fill1: 0, @@ -1591,7 +1926,33 @@ function convertRecordsToStyledPaths(records, fillPaths, linePaths, dictionary, } } } else { + if (!segment) { + if (!defaultPath) { + var style = { + color: { + red: 0, + green: 0, + blue: 0, + alpha: 255 + }, + width: 20 + }; + defaultPath = new SegmentedPath(null, processStyle(style, true)); + } + segment = defaultPath.addSegment([], [], isMorph ? [] : null); + segment.commands.push(SHAPE_MOVE_TO); + segment.data.push(x, y); + if (isMorph) { + segment.morphData.push(morphX, morphY); + } + } if (isMorph) { + while (morphRecord && morphRecord.type === 0) { + morphRecord = recordsMorph[j++]; + } + if (!morphRecord) { + morphRecord = record; + } } if (record.isStraight && (!isMorph || morphRecord.isStraight)) { x += record.deltaX | 0; @@ -1641,20 +2002,28 @@ function convertRecordsToStyledPaths(records, fillPaths, linePaths, dictionary, } } applySegmentToStyles(segment, styles, linePaths, fillPaths, isMorph); - push.apply(allFillPaths, allLinePaths); + if (allPaths) { + push.apply(allPaths, fillPaths); + } else { + allPaths = fillPaths; + } + push.apply(allPaths, linePaths); + if (defaultPath) { + allPaths.push(defaultPath); + } var removeCount = 0; - for (i = 0; i < allFillPaths.length; i++) { - path = allFillPaths[i]; + for (i = 0; i < allPaths.length; i++) { + path = allPaths[i]; if (!path.head()) { removeCount++; continue; } - allFillPaths[i - removeCount] = segmentedPathToShapePath(path, isMorph); + allPaths[i - removeCount] = segmentedPathToShapePath(path, isMorph, transferables); } - allFillPaths.length -= removeCount; - return allFillPaths; + allPaths.length -= removeCount; + return allPaths; } -function segmentedPathToShapePath(path, isMorph) { +function segmentedPathToShapePath(path, isMorph, transferables) { var start = path.head(); var end = start; var finalRoot = null; @@ -1709,7 +2078,7 @@ function segmentedPathToShapePath(path, isMorph) { totalDataLength += current.data.length; current = current.next; } - var shape = new ShapePath(path.fillStyle, path.lineStyle, totalCommandsLength, totalDataLength, isMorph); + var shape = new ShapePath(path.fillStyle, path.lineStyle, totalCommandsLength, totalDataLength, isMorph, transferables); var allCommands = shape.commands; var allData = shape.data; var allMorphData = shape.morphData; @@ -1717,13 +2086,10 @@ function segmentedPathToShapePath(path, isMorph) { var dataIndex = 0; current = finalRoot; while (current) { - var offset = 0; var commands = current.commands; var data = current.data; var morphData = current.morphData; - if (data[0] === allData[dataIndex - 2] && data[1] === allData[dataIndex - 1]) { - offset = 1; - } + var offset = +(data[0] === allData[dataIndex - 2] && data[1] === allData[dataIndex - 1]); for (var i = offset; i < commands.length; i++, commandsIndex++) { allCommands[commandsIndex] = commands[i]; } @@ -1751,7 +2117,7 @@ function processStyle(style, isLineStyle, dictionary, dependencies) { if (isLineStyle) { style.lineCap = CAPS_STYLE_TYPES[style.endCapStyle | 0]; style.lineJoin = JOIN_STYLE_TYPES[style.joinStyle | 0]; - style.miterLimit = style.miterLimitFactor * 2; + style.miterLimit = (style.miterLimitFactor || 1.5) * 2; if (!style.color && style.hasFill) { var fillStyle = processStyle(style.fillStyle, false, dictionary, dependencies); style.style = fillStyle.style; @@ -1820,9 +2186,10 @@ function createPathsList(styles, isLineStyle, dictionary, dependencies) { } function defineShape(tag, dictionary) { var dependencies = []; + var transferables = []; var fillPaths = createPathsList(tag.fillStyles, false, dictionary, dependencies); var linePaths = createPathsList(tag.lineStyles, true, dictionary, dependencies); - var paths = convertRecordsToStyledPaths(tag.records, fillPaths, linePaths, dictionary, dependencies, tag.recordsMorph || null); + var paths = convertRecordsToStyledPaths(tag.records, fillPaths, linePaths, dictionary, dependencies, tag.recordsMorph || null, transferables); if (tag.bboxMorph) { var mbox = tag.bboxMorph; extendBoundsByPoint(tag.bbox, mbox.xMin, mbox.yMin); @@ -1842,9 +2209,16 @@ function defineShape(tag, dictionary) { bboxMorph: tag.bboxMorph, isMorph: tag.isMorph, paths: paths, - require: dependencies.length ? dependencies : null + require: dependencies.length ? dependencies : null, + transferables: transferables }; } +function logShape(paths, bbox) { + var output = '{"bounds":' + JSON.stringify(bbox) + ',"paths":[' + paths.map(function (path) { + return path.serialize(); + }).join() + ']}'; + console.log(output); +} function SegmentedPath(fillStyle, lineStyle) { this.fillStyle = fillStyle; this.lineStyle = lineStyle; @@ -1898,7 +2272,7 @@ var SHAPE_WIDE_LINE_TO = 5; var SHAPE_CUBIC_CURVE_TO = 6; var SHAPE_CIRCLE = 7; var SHAPE_ELLIPSE = 8; -function ShapePath(fillStyle, lineStyle, commandsCount, dataLength, isMorph) { +function ShapePath(fillStyle, lineStyle, commandsCount, dataLength, isMorph, transferables) { this.fillStyle = fillStyle; this.lineStyle = lineStyle; if (commandsCount) { @@ -1911,8 +2285,21 @@ function ShapePath(fillStyle, lineStyle, commandsCount, dataLength, isMorph) { } this.bounds = null; this.strokeBounds = null; - this.isMorph = isMorph; + this.isMorph = !(!isMorph); this.fullyInitialized = false; + if (inWorker) { + this.buffers = [ + this.commands.buffer, + this.data.buffer + ]; + transferables.push(this.commands.buffer, this.data.buffer); + if (isMorph) { + this.buffers.push(this.morphData.buffer); + transferables.push(this.morphData.buffer); + } + } else { + this.buffers = null; + } } ShapePath.prototype = { moveTo: function (x, y) { @@ -1961,8 +2348,8 @@ ShapePath.prototype = { var formOpen = false; var formOpenX = 0; var formOpenY = 0; - for (var j = 0, k = 0; j < commands.length; j++) { - if (!this.isMorph) { + if (!this.isMorph) { + for (var j = 0, k = 0; j < commands.length; j++) { switch (commands[j]) { case SHAPE_MOVE_TO: formOpen = true; @@ -2027,9 +2414,14 @@ ShapePath.prototype = { } break; default: + if (commands[j] === 0 && j === commands.length - 1) { + break; + } console.warn('Unknown drawing command encountered: ' + commands[j]); } - } else { + } + } else { + for (var j = 0, k = 0; j < commands.length; j++) { switch (commands[j]) { case SHAPE_MOVE_TO: ctx.moveTo(morph(data[k] / 20, morphData[k++] / 20, ratio), morph(data[k] / 20, morphData[k++] / 20, ratio)); @@ -2512,8 +2904,29 @@ ShapePath.prototype = { this.strokeBounds = bounds; } return bounds; + }, + serialize: function () { + var output = '{'; + if (this.fillStyle) { + output += '"fill":' + JSON.stringify(this.fillStyle) + ','; + } + if (this.lineStyle) { + output += '"stroke":' + JSON.stringify(this.lineStyle) + ','; + } + output += '"commands":[' + Array.apply([], this.commands).join() + '],'; + output += '"data":[' + Array.apply([], this.data).join() + ']'; + return output + '}'; } }; +ShapePath.fromPlainObject = function (obj) { + var path = new ShapePath(obj.fill || null, obj.stroke || null); + path.commands = new Uint8Array(obj.commands); + path.data = new Int32Array(obj.data); + if (!inWorker) { + finishShapePath(path); + } + return path; +}; function distanceSq(x1, y1, x2, y2) { var dX = x2 - x1; var dY = y2 - y1; @@ -2731,23 +3144,24 @@ function extendBoundsByY(bounds, y) { function morph(start, end, ratio) { return start + (end - start) * ratio; } -function finishShapePaths(paths, dictionary) { - for (var i = 0; i < paths.length; i++) { - var path = paths[i]; - if (path.fullyInitialized) { - continue; - } - if (!(path instanceof ShapePath)) { - var untypedPath = path; - path = paths[i] = new ShapePath(path.fillStyle, path.lineStyle, 0, 0, path.isMorph); - path.commands = untypedPath.commands; - path.data = untypedPath.data; - path.morphData = untypedPath.morphData; - } - path.fillStyle && initStyle(path.fillStyle, dictionary); - path.lineStyle && initStyle(path.lineStyle, dictionary); - path.fullyInitialized = true; +function finishShapePath(path, dictionary) { + if (path.fullyInitialized) { + return path; } + if (!(path instanceof ShapePath)) { + var untypedPath = path; + path = new ShapePath(path.fillStyle, path.lineStyle, 0, 0, path.isMorph); + path.commands = new Uint8Array(untypedPath.buffers[0]); + path.data = new Int32Array(untypedPath.buffers[1]); + if (untypedPath.isMorph) { + path.morphData = new Int32Array(untypedPath.buffers[2]); + } + path.buffers = null; + } + path.fillStyle && initStyle(path.fillStyle, dictionary); + path.lineStyle && initStyle(path.lineStyle, dictionary); + path.fullyInitialized = true; + return path; } var inWorker = typeof window === 'undefined'; var factoryCtx = !inWorker ? document.createElement('canvas').getContext('2d') : null; @@ -2763,7 +3177,7 @@ function buildLinearGradientFactory(colorStops) { } return gradient; }; - fn.defaultGradient = defaultGradient; + fn.defaultFillStyle = defaultGradient; return fn; } function buildRadialGradientFactory(focalPoint, colorStops) { @@ -2778,7 +3192,31 @@ function buildRadialGradientFactory(focalPoint, colorStops) { } return gradient; }; - fn.defaultGradient = defaultGradient; + fn.defaultFillStyle = defaultGradient; + return fn; +} +function buildBitmapPatternFactory(img, repeat) { + var defaultPattern = factoryCtx.createPattern(img, repeat); + var cachedTransform, cachedTransformKey; + var fn = function createBitmapPattern(ctx, colorTransform) { + if (!colorTransform.mode) { + return defaultPattern; + } + var key = colorTransform.getTransformFingerprint(); + if (key === cachedTransformKey) { + return cachedTransform; + } + var canvas = document.createElement('canvas'); + canvas.width = img.width; + canvas.height = img.height; + var ctx = canvas.getContext('2d'); + colorTransform.setAlpha(ctx, true); + ctx.drawImage(img, 0, 0); + cachedTransform = ctx.createPattern(canvas, repeat); + cachedTransformKey = key; + return cachedTransform; + }; + fn.defaultFillStyle = defaultPattern; return fn; } function initStyle(style, dictionary) { @@ -2815,7 +3253,7 @@ function initStyle(style, dictionary) { case GRAPHICS_FILL_NONSMOOTHED_CLIPPED_BITMAP: var bitmap = dictionary[style.bitmapId]; var repeat = style.type === GRAPHICS_FILL_REPEATING_BITMAP || style.type === GRAPHICS_FILL_NONSMOOTHED_REPEATING_BITMAP; - style.style = factoryCtx.createPattern(bitmap.value.props.img, repeat ? 'repeat' : 'no-repeat'); + style.style = buildBitmapPatternFactory(bitmap.value.props.img, repeat ? 'repeat' : 'no-repeat'); break; default: fail('invalid fill style', 'shape'); @@ -3462,47 +3900,46 @@ SWF.embed = function (file, doc, container, options) { var canvas = doc.createElement('canvas'); var ctx = canvas.getContext('2d'); var loader = new flash.display.Loader(); - var loaderInfo = loader.contentLoaderInfo; + var loaderInfo = loader._contentLoaderInfo; var stage = new flash.display.Stage(); + var pixelRatio = 1; + var forceHidpiSetting = forceHidpi.value; stage._loader = loader; loaderInfo._parameters = options.movieParams; loaderInfo._url = options.url || (typeof file === 'string' ? file : null); loaderInfo._loaderURL = options.loaderURL || loaderInfo._url; - var pixelRatio = 'devicePixelRatio' in window ? window.devicePixelRatio : 1; - var canvasHolder = null; - canvas._pixelRatio = pixelRatio; - if (pixelRatio > 1) { - var cssScale = 'scale(' + 1 / pixelRatio + ', ' + 1 / pixelRatio + ')'; - canvas.setAttribute('style', '-moz-transform: ' + cssScale + ';' + '-webkit-transform: ' + cssScale + ';' + 'transform: ' + cssScale + ';' + '-moz-transform-origin: 0% 0%;' + '-webkit-transform-origin: 0% 0%;' + 'transform-origin: 0% 0%;'); - canvasHolder = doc.createElement('div'); - canvasHolder.setAttribute('style', 'display: inline-block; overflow: hidden;'); - canvasHolder.appendChild(canvas); - } - stage._contentsScaleFactor = pixelRatio; loader._parent = stage; loader._stage = stage; - function fitCanvas(container, canvas) { - if (canvasHolder) { - canvasHolder.style.width = container.clientWidth + 'px'; - canvasHolder.style.height = container.clientHeight + 'px'; + function setCanvasSize(width, height) { + if (pixelRatio === 1) { + canvas.width = width | 0; + canvas.height = height | 0; + return; } - canvas.width = container.clientWidth * pixelRatio; - canvas.height = container.clientHeight * pixelRatio; + var canvasWidth = Math.floor(width * pixelRatio); + var canvasHeight = Math.floor(height * pixelRatio); + canvas.style.width = canvasWidth / pixelRatio + 'px'; + canvas.style.height = canvasHeight / pixelRatio + 'px'; + canvas.width = canvasWidth; + canvas.height = canvasHeight; + } + function fitCanvas(container) { + setCanvasSize(container.clientWidth, container.clientHeight); stage._invalid = true; } loaderInfo._addEventListener('init', function () { + if (forceHidpiSetting || loaderInfo._swfVersion >= 18) { + pixelRatio = 'devicePixelRatio' in window ? window.devicePixelRatio : 1; + } + canvas._pixelRatio = pixelRatio; + stage._contentsScaleFactor = pixelRatio; if (container.clientHeight) { - fitCanvas(container, canvas); + fitCanvas(container); window.addEventListener('resize', function () { - fitCanvas(container, canvas); + fitCanvas(container); }); } else { - if (canvasHolder) { - canvasHolder.style.width = stage._stageWidth / 20 + 'px'; - canvasHolder.style.height = stage._stageHeight / 20 + 'px'; - } - canvas.width = stage._stageWidth * pixelRatio / 20; - canvas.height = stage._stageHeight * pixelRatio / 20; + setCanvasSize(stage._stageWidth / 20, stage._stageHeight / 20); } container.setAttribute('style', 'position: relative'); canvas.addEventListener('click', function () { @@ -3515,10 +3952,7 @@ SWF.embed = function (file, doc, container, options) { } }); canvas.addEventListener('mousedown', function () { - if (stage._mouseTarget._buttonMode) { - stage._mouseTarget._gotoButtonState('down'); - } - stage._mouseTarget._dispatchEvent('mouseDown'); + stage._mouseEvents.push('mousedown'); }); canvas.addEventListener('mousemove', function (domEvt) { var node = this; @@ -3531,8 +3965,8 @@ SWF.embed = function (file, doc, container, options) { } while (node = node.offsetParent); } var m = stage._concatenatedTransform; - var mouseX = ((domEvt.pageX - left) * pixelRatio - m.tx) / m.a; - var mouseY = ((domEvt.pageY - top) * pixelRatio - m.ty) / m.d; + var mouseX = ((domEvt.pageX - left) * pixelRatio - m.tx / 20) / m.a; + var mouseY = ((domEvt.pageY - top) * pixelRatio - m.ty / 20) / m.d; if (mouseX !== stage._mouseX || mouseY !== stage._mouseY) { stage._mouseMoved = true; stage._mouseX = mouseX * 20; @@ -3540,10 +3974,7 @@ SWF.embed = function (file, doc, container, options) { } }); canvas.addEventListener('mouseup', function () { - if (stage._mouseTarget._buttonMode) { - stage._mouseTarget._gotoButtonState('over'); - } - stage._mouseTarget._dispatchEvent('mouseUp'); + stage._mouseEvents.push('mouseup'); }); canvas.addEventListener('mouseover', function () { stage._mouseMoved = true; @@ -3553,6 +3984,36 @@ SWF.embed = function (file, doc, container, options) { stage._mouseMoved = true; stage._mouseOver = false; }); + window.addEventListener('message', function (evt) { + var data = evt.data; + if (typeof data !== 'object' || data === null) { + return; + } + var type = data.type; + switch (type) { + case 'mousemove': + case 'mouseup': + case 'mousedown': + var isMouseMove = type === 'mousemove'; + stage._mouseMoved = true; + stage._mouseOver = true; + stage._mouseX = data.x * 20; + stage._mouseY = data.y * 20; + if (!isMouseMove) { + stage._mouseEvents.push(type); + } + break; + case 'mouseover': + case 'mouseout': + stage._mouseMoved = true; + stage._mouseOver = type === 'mouseover'; + break; + case 'keyup': + case 'keydown': + stage._dispatchEvent(new flash.events.KeyboardEvent(type === 'keyup' ? 'keyUp' : 'keyDown', true, false, data.charCode, data.keyCode, data.keyLocation, data.ctrlKey || false, data.altKey || false, data.shiftKey || false)); + break; + } + }, false); var bgcolor = loaderInfo._backgroundColor; if (options.objectParams) { var m; @@ -3578,10 +4039,9 @@ SWF.embed = function (file, doc, container, options) { ctx.fillStyle = rgbaObjToStr(bgcolor); ctx.fillRect(0, 0, canvas.width, canvas.height); var root = loader._content; - stage._children[0] = root; - root._dispatchEvent('added'); + root._dispatchEvent('added', undefined, true); root._dispatchEvent('addedToStage'); - container.appendChild(canvasHolder || canvas); + container.appendChild(canvas); if (options.onStageInitialized) { options.onStageInitialized(stage); } @@ -3604,11 +4064,19 @@ var showRedrawRegions = rendererOptions.register(new Option('rr', 'showRedrawReg var renderAsWireframe = rendererOptions.register(new Option('raw', 'renderAsWireframe', 'boolean', false, 'render as wireframe')); var showQuadTree = rendererOptions.register(new Option('qt', 'showQuadTree', 'boolean', false, 'show quad tree')); var turboMode = rendererOptions.register(new Option('', 'turbo', 'boolean', false, 'turbo mode')); +var forceHidpi = rendererOptions.register(new Option('', 'forceHidpi', 'boolean', false, 'force hidpi')); +var enableConstructChildren = rendererOptions.register(new Option('', 'constructChildren', 'boolean', true, 'Construct Children')); +var enableEnterFrame = rendererOptions.register(new Option('', 'enterFrame', 'boolean', true, 'Enter Frame')); +var enableAdvanceFrame = rendererOptions.register(new Option('', 'advanceFrame', 'boolean', true, 'Advance Frame')); if (typeof FirefoxCom !== 'undefined') { turboMode.value = FirefoxCom.requestSync('getBoolPref', { pref: 'shumway.turboMode', def: false }); + forceHidpi.value = FirefoxCom.requestSync('getBoolPref', { + pref: 'shumway.force_hidpi', + def: false + }); } var CanvasCache = { cache: [], @@ -3648,8 +4116,7 @@ function visitContainer(container, visitor, context) { continue; } if (visitor.ignoreVisibleAttribute || child._visible && !child._maskedObject) { - var isContainer = flash.display.DisplayObjectContainer.class.isInstanceOf(child) || flash.display.SimpleButton.class.isInstanceOf(child); - visitor.visit(child, isContainer, visitContainer, context); + visitor.visit(child, visitContainer, context); } } visitor.childrenEnd(container); @@ -3681,9 +4148,21 @@ RenderVisitor.prototype = { start: function () { visitContainer(this.root, this, new RenderingContext(this.refreshStage, this.invalidPath)); }, - startFragment: function () { - var isContainer = flash.display.DisplayObjectContainer.class.isInstanceOf(this.root) || flash.display.SimpleButton.class.isInstanceOf(this.root); - var t = this.root._currentTransform; + startFragment: function (matrix) { + var root = this.root; + var currentTransform = root._currentTransform; + var t = currentTransform; + if (matrix) { + t = root._currentTransform = { + a: matrix.a, + b: matrix.b, + c: matrix.c, + d: matrix.d, + tx: matrix.tx * 20 | 0, + ty: matrix.ty * 20 | 0 + }; + root._invalidateTransform(); + } var inverse; if (t) { inverse = new flash.geom.Matrix(t.a, t.b, t.c, t.d, t.tx / 20, t.ty / 20); @@ -3691,10 +4170,14 @@ RenderVisitor.prototype = { this.ctx.save(); this.ctx.transform(inverse.a, inverse.b, inverse.c, inverse.d, inverse.tx, inverse.ty); } - this.visit(this.root, isContainer, visitContainer, new RenderingContext(this.refreshStage, this.invalidPath)); + this.visit(root, visitContainer, new RenderingContext(this.refreshStage, this.invalidPath)); if (t) { this.ctx.restore(); } + if (matrix) { + root._currentTransform = currentTransform; + root._invalidateTransform(); + } }, childrenStart: function (parent) { if (this.depth === 0) { @@ -3733,8 +4216,9 @@ RenderVisitor.prototype = { childrenEnd: function (parent) { if (this.clipDepth) { while (this.clipDepth.length > 0) { - this.clipDepth.pop(); - this.ctx.restore(); + var clipDepthInfo = this.clipDepth.pop(); + this.clipEnd(clipDepthInfo); + this.ctx = clipDepthInfo.ctx; } this.clipDepth = null; } @@ -3748,7 +4232,7 @@ RenderVisitor.prototype = { this.invalidPath = null; } }, - visit: function (child, isContainer, visitContainer, context) { + visit: function (child, visitContainer, context) { var ctx = this.ctx; var parentHasClippingMask = context.isClippingMask; var parentColorTransform = context.colorTransform; @@ -3757,20 +4241,29 @@ RenderVisitor.prototype = { context.colorTransform = parentColorTransform.applyCXForm(child._cxform); } if (!clippingMask) { - while (this.clipDepth && this.clipDepth.length > 0 && child._depth > this.clipDepth[0]) { - this.clipDepth.shift(); - ctx.restore(); + while (this.clipDepth && this.clipDepth.length > 0 && child._depth > this.clipDepth[0].clipDepth) { + var clipDepthInfo = this.clipDepth.shift(); + this.clipEnd(clipDepthInfo); + ctx = this.ctx = clipDepthInfo.ctx; } if (child._clipDepth) { - if (!this.clipDepth) { - this.clipDepth = []; - } context.isClippingMask = clippingMask = true; - this.clipDepth.unshift(child._clipDepth); - ctx.save(); + var clipDepthInfo = this.clipStart(child); + if (!this.clipDepth) { + this.clipDepth = [ + clipDepthInfo + ]; + } else { + this.clipDepth.unshift(clipDepthInfo); + } + ctx = this.ctx = clipDepthInfo.mask.ctx; + } else { + if (this.clipDepth && this.clipDepth.length > 0 && child._depth <= this.clipDepth[0].clipDepth) { + ctx = this.ctx = this.clipDepth[0].maskee.ctx; + } } } - if (clippingMask && isContainer) { + if (clippingMask && child._isContainer) { ctx.save(); renderDisplayObject(child, ctx, context); for (var i = 0, n = child._children.length; i < n; i++) { @@ -3779,12 +4272,11 @@ RenderVisitor.prototype = { continue; } if (this.ignoreVisibleAttribute || child1._visible && !child1._maskedObject) { - var isContainer = flash.display.DisplayObjectContainer.class.isInstanceOf(child1) || flash.display.SimpleButton.class.isInstanceOf(child1); - this.visit(child1, isContainer, visitContainer, context); + this.visit(child1, visitContainer, context); } } ctx.restore(); - ctx.clip(); + ctx.fill(); context.isClippingMask = parentHasClippingMask; context.colorTransform = parentColorTransform; return; @@ -3792,45 +4284,64 @@ RenderVisitor.prototype = { ctx.save(); ctx.globalCompositeOperation = getBlendModeName(child._blendMode); if (child._mask) { - var m = child._parent._getConcatenatedTransform(true); - var tempCanvas, tempCtx, maskCanvas, maskCtx; - maskCanvas = CanvasCache.getCanvas(ctx.canvas); - maskCtx = maskCanvas.ctx; - maskCtx.setTransform(m.a, m.b, m.c, m.d, m.tx, m.ty); - var isMaskContainer = flash.display.DisplayObjectContainer.class.isInstanceOf(child._mask) || flash.display.SimpleButton.class.isInstanceOf(child._mask); - this.ctx = maskCtx; - this.visit(child._mask, isMaskContainer, visitContainer, new RenderingContext(this.refreshStage)); + var clipInfo = this.clipStart(child); + var mask = clipInfo.mask; + var maskee = clipInfo.maskee; + var savedClipDepth = this.clipDepth; + this.clipDepth = null; + this.ctx = mask.ctx; + this.visit(child._mask, visitContainer, new RenderingContext(this.refreshStage)); this.ctx = ctx; - tempCanvas = CanvasCache.getCanvas(ctx.canvas); - tempCtx = tempCanvas.ctx; - tempCtx.setTransform(m.a, m.b, m.c, m.d, m.tx, m.ty); - renderDisplayObject(child, tempCtx, context); - if (isContainer) { - this.ctx = tempCtx; + this.clipDepth = savedClipDepth; + renderDisplayObject(child, maskee.ctx, context); + if (child._isContainer) { + this.ctx = maskee.ctx; visitContainer(child, this, context); this.ctx = ctx; } - tempCtx.globalCompositeOperation = 'destination-in'; - tempCtx.setTransform(1, 0, 0, 1, 0, 0); - tempCtx.drawImage(maskCanvas.canvas, 0, 0); - ctx.save(); - ctx.setTransform(1, 0, 0, 1, 0, 0); - ctx.drawImage(tempCanvas.canvas, 0, 0); - ctx.restore(); - CanvasCache.releaseCanvas(tempCanvas); - CanvasCache.releaseCanvas(maskCanvas); + this.clipEnd(clipInfo); } else { renderDisplayObject(child, ctx, context); - if (isContainer) { + if (child._isContainer) { visitContainer(child, this, context); } } ctx.restore(); if (clippingMask) { - ctx.clip(); + ctx.fill(); } context.isClippingMask = parentHasClippingMask; context.colorTransform = parentColorTransform; + }, + clipStart: function (child) { + var m = child._parent._getConcatenatedTransform(true); + var tx = m.tx / 20; + var ty = m.ty / 20; + var mask = CanvasCache.getCanvas(this.ctx.canvas); + mask.ctx.setTransform(m.a, m.b, m.c, m.d, tx, ty); + var maskee = CanvasCache.getCanvas(this.ctx.canvas); + maskee.ctx.setTransform(m.a, m.b, m.c, m.d, tx, ty); + var clipInfo = { + ctx: this.ctx, + mask: mask, + maskee: maskee, + clipDepth: child._clipDepth + }; + return clipInfo; + }, + clipEnd: function (clipInfo) { + var ctx = clipInfo.ctx; + var mask = clipInfo.mask; + var maskee = clipInfo.maskee; + maskee.ctx.globalCompositeOperation = 'destination-in'; + maskee.ctx.setTransform(1, 0, 0, 1, 0, 0); + maskee.ctx.drawImage(mask.canvas, 0, 0); + ctx.save(); + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.drawImage(maskee.canvas, 0, 0); + ctx.restore(); + CanvasCache.releaseCanvas(mask); + CanvasCache.releaseCanvas(maskee); } }; function RenderingColorTransform() { @@ -3877,7 +4388,7 @@ RenderingColorTransform.prototype = { } else if (typeof style === 'number') { style = this.convertNumericColor(style); } else if (typeof style === 'function') { - style = style.defaultGradient; + style = style.defaultFillStyle; } ctx.fillStyle = style; }, @@ -3887,7 +4398,7 @@ RenderingColorTransform.prototype = { } else if (typeof style === 'number') { style = this.convertNumericColor(style); } else if (typeof style === 'function') { - style = style.defaultGradient; + style = style.defaultFillStyle; } ctx.strokeStyle = style; }, @@ -3944,6 +4455,9 @@ RenderingColorTransform.prototype = { var b = Math.min(255, Math.max(0, m[3] * t[2] + t[6])) | 0; var a = Math.min(1, Math.max(0, m[4] * t[3] + t[7] / 256)); return 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; + }, + getTransformFingerprint: function () { + return this.transform.join('|'); } }; function RenderingContext(refreshStage, invalidPath) { @@ -4013,7 +4527,6 @@ function renderQuadTree(ctx, qtree) { renderQuadTree(ctx, nodes[i]); } } -var fps; var renderingTerminated = false; var samplesLeftPlusOne = 0; function triggerSampling(count) { @@ -4040,6 +4553,18 @@ function sampleEnd() { console.profileEnd('Sample'); } } +var timeline; +function timelineEnter(name) { + timeline && timeline.enter(name); +} +function timelineLeave(name) { + timeline && timeline.leave(name); +} +function timelineWrapBroadcastMessage(domain, message) { + timelineEnter(message); + domain.broadcastMessage(message); + timelineLeave(message); +} function renderStage(stage, ctx, events) { var frameWidth, frameHeight; function updateRenderTransform() { @@ -4090,8 +4615,8 @@ function renderStage(stage, ctx, events) { var m = stage._concatenatedTransform; m.a = scaleX; m.d = scaleY; - m.tx = offsetX; - m.ty = offsetY; + m.tx = offsetX * 20; + m.ty = offsetY * 20; } updateRenderTransform(); var frameTime = 0; @@ -4180,6 +4705,8 @@ function renderStage(stage, ctx, events) { if (stage._mouseMoved) { stage._mouseMoved = false; mouseMoved = stage._mouseOver; + } else { + stage._handleMouseButtons(); } if (renderFrame || refreshStage || mouseMoved) { FrameCounter.clear(); @@ -4194,18 +4721,18 @@ function renderStage(stage, ctx, events) { nextRenderAt += maxDelay; } } - fps && fps.enter('EVENTS'); + timelineEnter('EVENTS'); if (firstRun) { firstRun = false; } else { - domain.broadcastMessage('advanceFrame'); - domain.broadcastMessage('enterFrame'); - domain.broadcastMessage('constructChildren'); + enableAdvanceFrame.value && timelineWrapBroadcastMessage(domain, 'advanceFrame'); + enableEnterFrame.value && timelineWrapBroadcastMessage(domain, 'enterFrame'); + enableConstructChildren.value && timelineWrapBroadcastMessage(domain, 'constructChildren'); } - domain.broadcastMessage('frameConstructed'); - domain.broadcastMessage('executeFrame'); - domain.broadcastMessage('exitFrame'); - fps && fps.leave('EVENTS'); + timelineWrapBroadcastMessage(domain, 'frameConstructed'); + timelineWrapBroadcastMessage(domain, 'executeFrame'); + timelineWrapBroadcastMessage(domain, 'exitFrame'); + timelineLeave('EVENTS'); } if (stage._deferRenderEvent) { stage._deferRenderEvent = false; @@ -4215,19 +4742,19 @@ function renderStage(stage, ctx, events) { var invalidPath = null; if (!disablePreVisitor.value) { traceRenderer.value && frameWriter.enter('> Pre Visitor'); - fps && fps.enter('PRE'); + timelineEnter('PRE'); invalidPath = stage._processInvalidRegions(true); - fps && fps.leave('PRE'); + timelineLeave('PRE'); traceRenderer.value && frameWriter.leave('< Pre Visitor'); } else { stage._processInvalidRegions(false); } if (!disableRenderVisitor.value) { - fps && fps.enter('RENDER'); + timelineEnter('RENDER'); traceRenderer.value && frameWriter.enter('> Render Visitor'); new RenderVisitor(stage, ctx, invalidPath, refreshStage).start(); traceRenderer.value && frameWriter.leave('< Render Visitor'); - fps && fps.leave('RENDER'); + timelineLeave('RENDER'); } if (showQuadTree.value) { ctx.strokeStyle = 'green'; @@ -4240,20 +4767,22 @@ function renderStage(stage, ctx, events) { } } if (mouseMoved && !disableMouseVisitor.value) { - fps && renderFrame && fps.enter('MOUSE'); + renderFrame && timelineEnter('MOUSE'); traceRenderer.value && frameWriter.enter('> Mouse Visitor'); stage._handleMouse(); traceRenderer.value && frameWriter.leave('< Mouse Visitor'); - fps && renderFrame && fps.leave('MOUSE'); + renderFrame && timelineLeave('MOUSE'); ctx.canvas.style.cursor = stage._cursor; } if (renderFrame && events.onAfterFrame) { events.onAfterFrame(); } if (traceRenderer.value) { + frameWriter.enter('> Frame Counters'); for (var name in FrameCounter.counts) { - appendToFrameTerminal(name + ': ' + FrameCounter.counts[name], 'gray'); + frameWriter.writeLn(name + ': ' + FrameCounter.counts[name]); } + frameWriter.leave('< Frame Counters'); var frameElapsedTime = performance.now() - frameStartTime; var frameFPS = 1000 / frameElapsedTime; frameFPSAverage.push(frameFPS); @@ -4824,6 +5353,19 @@ var tagHandler = function (global) { $.data = readBinary($bytes, $stream, 0); return $; } + function exportAssets($bytes, $stream, $, swfVersion, tagCode) { + $ || ($ = {}); + var exportsCount = readUi16($bytes, $stream); + var $0 = $.exports = []; + var $1 = exportsCount; + while ($1--) { + var $2 = {}; + $2.symbolId = readUi16($bytes, $stream); + $2.className = readString($bytes, $stream, 0); + $0.push($2); + } + return $; + } function symbolClass($bytes, $stream, $, swfVersion, tagCode) { $ || ($ = {}); var symbolCount = readUi16($bytes, $stream); @@ -5639,7 +6181,7 @@ var tagHandler = function (global) { 45: soundStreamHead, 46: defineShape, 48: defineFont2, - 56: undefined, + 56: exportAssets, 57: undefined, 58: undefined, 59: doAction, @@ -5689,58 +6231,79 @@ var readHeader = function readHeader($bytes, $stream, $, swfVersion, tagCode) { $.frameCount = readUi16($bytes, $stream); return $; }; -function readTags(context, stream, swfVersion, onprogress) { +function readTags(context, stream, swfVersion, final, onprogress) { var tags = context.tags; var bytes = stream.bytes; var lastSuccessfulPosition; + var tag = null; + if (context._readTag) { + tag = context._readTag; + delete context._readTag; + } try { - do { + while (stream.pos < stream.end) { lastSuccessfulPosition = stream.pos; stream.ensure(2); var tagCodeAndLength = readUi16(bytes, stream); + if (!tagCodeAndLength) { + final = true; + break; + } var tagCode = tagCodeAndLength >> 6; var length = tagCodeAndLength & 63; if (length === 63) { stream.ensure(4); length = readUi32(bytes, stream); } - stream.ensure(length); - if (tagCode === 0) { - break; + if (tag) { + if (tagCode === 1 && tag.code === 1) { + tag.repeat++; + stream.pos += length; + continue; + } + tags.push(tag); + if (onprogress && tag.id !== undefined) { + onprogress(context); + } + tag = null; } + stream.ensure(length); var substream = stream.substream(stream.pos, stream.pos += length); var subbytes = substream.bytes; - var tag = { + var nextTag = { code: tagCode }; if (tagCode === 39) { - tag.type = 'sprite'; - tag.id = readUi16(subbytes, substream); - tag.frameCount = readUi16(subbytes, substream); - tag.tags = []; - readTags(tag, substream, swfVersion); + nextTag.type = 'sprite'; + nextTag.id = readUi16(subbytes, substream); + nextTag.frameCount = readUi16(subbytes, substream); + nextTag.tags = []; + readTags(nextTag, substream, swfVersion, true); + } else if (tagCode === 1) { + nextTag.repeat = 1; } else { var handler = tagHandler[tagCode]; if (handler) { - handler(subbytes, substream, tag, swfVersion, tagCode); + handler(subbytes, substream, nextTag, swfVersion, tagCode); } } + tag = nextTag; + } + if (tag && final) { + tag.finalTag = true; tags.push(tag); - if (tagCode === 1) { - while (stream.pos + 2 <= stream.end && stream.getUint16(stream.pos, true) >> 6 === 1) { - tags.push(tag); - stream.pos += 2; - } - if (onprogress) - onprogress(context); - } else if (onprogress && tag.id !== undefined) { + if (onprogress) { onprogress(context); } - } while (stream.pos < stream.end); + } else { + context._readTag = tag; + } } catch (e) { - if (e !== StreamNoDataError) + if (e !== StreamNoDataError) { throw e; + } stream.pos = lastSuccessfulPosition; + context._readTag = tag; } } function HeadTailBuffer(defaultSize) { @@ -5889,12 +6452,14 @@ BodyParser.prototype = { buffer.push(data); stream = buffer.createStream(); } + var finalBlock = false; if (progressInfo) { swf.bytesLoaded = progressInfo.bytesLoaded; swf.bytesTotal = progressInfo.bytesTotal; + finalBlock = progressInfo.bytesLoaded >= progressInfo.bytesTotal; } var readStartTime = performance.now(); - readTags(swf, stream, swfVersion, options.onprogress); + readTags(swf, stream, swfVersion, finalBlock, options.onprogress); swf.parseTime += performance.now() - readStartTime; var read = stream.pos; buffer.removeHead(read); @@ -6007,6 +6572,408 @@ SWF.parse = function (buffer, options) { }; pipe.push(bytes, progressInfo); }; +var $RELEASE = false; +var isWorker = typeof window === 'undefined'; +if (isWorker && !true) { + importScripts.apply(null, [ + '../../lib/DataView.js/DataView.js', + '../flash/util.js', + 'config.js', + 'swf.js', + 'types.js', + 'structs.js', + 'tags.js', + 'inflate.js', + 'stream.js', + 'templates.js', + 'generator.js', + 'handlers.js', + 'parser.js', + 'bitmap.js', + 'button.js', + 'font.js', + 'image.js', + 'label.js', + 'shape.js', + 'sound.js', + 'text.js' + ]); +} +function defineSymbol(swfTag, symbols) { + var symbol; + switch (swfTag.code) { + case SWF_TAG_CODE_DEFINE_BITS: + case SWF_TAG_CODE_DEFINE_BITS_JPEG2: + case SWF_TAG_CODE_DEFINE_BITS_JPEG3: + case SWF_TAG_CODE_DEFINE_BITS_JPEG4: + case SWF_TAG_CODE_JPEG_TABLES: + symbol = defineImage(swfTag, symbols); + break; + case SWF_TAG_CODE_DEFINE_BITS_LOSSLESS: + case SWF_TAG_CODE_DEFINE_BITS_LOSSLESS2: + symbol = defineBitmap(swfTag); + break; + case SWF_TAG_CODE_DEFINE_BUTTON: + case SWF_TAG_CODE_DEFINE_BUTTON2: + symbol = defineButton(swfTag, symbols); + break; + case SWF_TAG_CODE_DEFINE_EDIT_TEXT: + symbol = defineText(swfTag, symbols); + break; + case SWF_TAG_CODE_DEFINE_FONT: + case SWF_TAG_CODE_DEFINE_FONT2: + case SWF_TAG_CODE_DEFINE_FONT3: + case SWF_TAG_CODE_DEFINE_FONT4: + symbol = defineFont(swfTag, symbols); + break; + case SWF_TAG_CODE_DEFINE_MORPH_SHAPE: + case SWF_TAG_CODE_DEFINE_MORPH_SHAPE2: + case SWF_TAG_CODE_DEFINE_SHAPE: + case SWF_TAG_CODE_DEFINE_SHAPE2: + case SWF_TAG_CODE_DEFINE_SHAPE3: + case SWF_TAG_CODE_DEFINE_SHAPE4: + symbol = defineShape(swfTag, symbols); + break; + case SWF_TAG_CODE_DEFINE_SOUND: + symbol = defineSound(swfTag, symbols); + break; + case SWF_TAG_CODE_DEFINE_BINARY_DATA: + symbol = { + type: 'binary', + id: swfTag.id, + data: swfTag.data + }; + break; + case SWF_TAG_CODE_DEFINE_SPRITE: + var depths = {}; + var frame = { + type: 'frame' + }; + var frames = []; + var tags = swfTag.tags; + var frameScripts = null; + var frameIndex = 0; + var soundStream = null; + for (var i = 0, n = tags.length; i < n; i++) { + var tag = tags[i]; + switch (tag.code) { + case SWF_TAG_CODE_DO_ACTION: + if (!frameScripts) + frameScripts = []; + frameScripts.push(frameIndex); + frameScripts.push(tag.actionsData); + break; + case SWF_TAG_CODE_START_SOUND: + var startSounds = frame.startSounds || (frame.startSounds = []); + startSounds.push(tag); + break; + case SWF_TAG_CODE_SOUND_STREAM_HEAD: + try { + soundStream = createSoundStream(tag); + frame.soundStream = soundStream.info; + } catch (e) { + } + break; + case SWF_TAG_CODE_SOUND_STREAM_BLOCK: + if (soundStream) { + frame.soundStreamBlock = soundStream.decode(tag.data); + } + break; + case SWF_TAG_CODE_FRAME_LABEL: + frame.labelName = tag.name; + break; + case SWF_TAG_CODE_PLACE_OBJECT: + case SWF_TAG_CODE_PLACE_OBJECT2: + case SWF_TAG_CODE_PLACE_OBJECT3: + depths[tag.depth] = tag; + break; + case SWF_TAG_CODE_REMOVE_OBJECT: + case SWF_TAG_CODE_REMOVE_OBJECT2: + depths[tag.depth] = null; + break; + case SWF_TAG_CODE_SHOW_FRAME: + frameIndex += tag.repeat; + frame.repeat = tag.repeat; + frame.depths = depths; + frames.push(frame); + depths = {}; + frame = { + type: 'frame' + }; + break; + } + } + symbol = { + type: 'sprite', + id: swfTag.id, + frameCount: swfTag.frameCount, + frames: frames, + frameScripts: frameScripts + }; + break; + case SWF_TAG_CODE_DEFINE_TEXT: + case SWF_TAG_CODE_DEFINE_TEXT2: + symbol = defineLabel(swfTag, symbols); + break; + } + if (!symbol) { + return { + command: 'error', + message: 'unknown symbol type: ' + swfTag.code + }; + } + symbol.isSymbol = true; + symbols[swfTag.id] = symbol; + return symbol; +} +function createParsingContext(commitData) { + var depths = {}; + var symbols = {}; + var frame = { + type: 'frame' + }; + var tagsProcessed = 0; + var soundStream = null; + var lastProgressSent = 0; + return { + onstart: function (result) { + commitData({ + command: 'init', + result: result + }); + }, + onprogress: function (result) { + if (Date.now() - lastProgressSent > 1000 / 24 || result.bytesLoaded === result.bytesTotal) { + commitData({ + command: 'progress', + result: { + bytesLoaded: result.bytesLoaded, + bytesTotal: result.bytesTotal + } + }); + lastProgressSent = Date.now(); + } + var tags = result.tags; + for (var n = tags.length; tagsProcessed < n; tagsProcessed++) { + var tag = tags[tagsProcessed]; + if ('id' in tag) { + var symbol = defineSymbol(tag, symbols); + commitData(symbol, symbol.transferables); + continue; + } + switch (tag.code) { + case SWF_TAG_CODE_DEFINE_SCENE_AND_FRAME_LABEL_DATA: + frame.sceneData = tag; + break; + case SWF_TAG_CODE_DEFINE_SCALING_GRID: + var symbolUpdate = { + isSymbol: true, + id: tag.symbolId, + updates: { + scale9Grid: tag.splitter + } + }; + commitData(symbolUpdate); + break; + case SWF_TAG_CODE_DO_ABC: + case SWF_TAG_CODE_DO_ABC_: + var abcBlocks = frame.abcBlocks; + if (abcBlocks) + abcBlocks.push({ + data: tag.data, + flags: tag.flags + }); + else + frame.abcBlocks = [ + { + data: tag.data, + flags: tag.flags + } + ]; + break; + case SWF_TAG_CODE_DO_ACTION: + var actionBlocks = frame.actionBlocks; + if (actionBlocks) + actionBlocks.push(tag.actionsData); + else + frame.actionBlocks = [ + tag.actionsData + ]; + break; + case SWF_TAG_CODE_DO_INIT_ACTION: + var initActionBlocks = frame.initActionBlocks || (frame.initActionBlocks = []); + initActionBlocks.push({ + spriteId: tag.spriteId, + actionsData: tag.actionsData + }); + break; + case SWF_TAG_CODE_START_SOUND: + var startSounds = frame.startSounds; + if (!startSounds) + frame.startSounds = startSounds = []; + startSounds.push(tag); + break; + case SWF_TAG_CODE_SOUND_STREAM_HEAD: + try { + soundStream = createSoundStream(tag); + frame.soundStream = soundStream.info; + } catch (e) { + } + break; + case SWF_TAG_CODE_SOUND_STREAM_BLOCK: + if (soundStream) { + frame.soundStreamBlock = soundStream.decode(tag.data); + } + break; + case SWF_TAG_CODE_EXPORT_ASSETS: + var exports = frame.exports; + if (exports) + frame.exports = exports.concat(tag.exports); + else + frame.exports = tag.exports.slice(0); + break; + case SWF_TAG_CODE_SYMBOL_CLASS: + var symbolClasses = frame.symbolClasses; + if (symbolClasses) + frame.symbolClasses = symbolClasses.concat(tag.exports); + else + frame.symbolClasses = tag.exports.slice(0); + break; + case SWF_TAG_CODE_FRAME_LABEL: + frame.labelName = tag.name; + break; + case SWF_TAG_CODE_PLACE_OBJECT: + case SWF_TAG_CODE_PLACE_OBJECT2: + case SWF_TAG_CODE_PLACE_OBJECT3: + depths[tag.depth] = tag; + break; + case SWF_TAG_CODE_REMOVE_OBJECT: + case SWF_TAG_CODE_REMOVE_OBJECT2: + depths[tag.depth] = null; + break; + case SWF_TAG_CODE_SET_BACKGROUND_COLOR: + frame.bgcolor = tag.color; + break; + case SWF_TAG_CODE_SHOW_FRAME: + frame.repeat = tag.repeat; + frame.depths = depths; + frame.complete = !(!tag.finalTag); + commitData(frame); + depths = {}; + frame = { + type: 'frame' + }; + break; + } + } + }, + oncomplete: function (result) { + commitData(result); + var stats; + if (typeof result.swfVersion === 'number') { + var bbox = result.bbox; + stats = { + topic: 'parseInfo', + parseTime: result.parseTime, + bytesTotal: result.bytesTotal, + swfVersion: result.swfVersion, + frameRate: result.frameRate, + width: (bbox.xMax - bbox.xMin) / 20, + height: (bbox.yMax - bbox.yMin) / 20, + isAvm2: !(!result.fileAttributes.doAbc) + }; + } + commitData({ + command: 'complete', + stats: stats + }); + } + }; +} +function parseBytes(bytes, commitData) { + SWF.parse(bytes, createParsingContext(commitData)); +} +function ResourceLoader(scope) { + this.subscription = null; + var self = this; + if (!isWorker) { + this.messenger = { + postMessage: function (data) { + self.onmessage({ + data: data + }); + } + }; + } else { + this.messenger = scope; + scope.onmessage = function (event) { + self.listener(event.data); + }; + } +} +ResourceLoader.prototype = { + terminate: function () { + this.messenger = null; + this.listener = null; + }, + onmessage: function (event) { + this.listener(event.data); + }, + postMessage: function (data) { + this.listener && this.listener(data); + }, + listener: function (data) { + if (this.subscription) { + this.subscription.callback(data.data, data.progress); + } else if (data === 'pipe:') { + this.subscription = { + subscribe: function (callback) { + this.callback = callback; + } + }; + this.parseLoadedData(this.messenger, this.subscription); + } else { + this.parseLoadedData(this.messenger, data); + } + }, + parseLoadedData: function (loader, request, context) { + function commitData(data, transferables) { + try { + loader.postMessage(data, transferables); + } catch (ex) { + if (ex != 'DataCloneError') { + throw ex; + } + loader.postMessage(data); + } + } + if (request instanceof ArrayBuffer) { + parseBytes(request, commitData); + } else if ('subscribe' in request) { + var pipe = SWF.parseAsync(createParsingContext(commitData)); + request.subscribe(function (data, progress) { + if (data) { + pipe.push(data, progress); + } else { + pipe.close(); + } + }); + } else if (typeof FileReaderSync !== 'undefined') { + var reader = new FileReaderSync(); + var buffer = reader.readAsArrayBuffer(request); + parseBytes(buffer, commitData); + } else { + var reader = new FileReader(); + reader.onload = function () { + parseBytes(this.result, commitData); + }; + reader.readAsArrayBuffer(request); + } + } +}; +if (isWorker) { + var loader = new ResourceLoader(this); +} function ActionsDataStream(array, swfVersion) { this.array = array; this.position = 0; @@ -6162,12 +7129,8 @@ function AS2Context(swfVersion) { } AS2Context.instance = null; AS2Context.prototype = { - addAssets: function (assets) { - for (var i = 0; i < assets.length; i++) { - if (assets[i].className) { - this.assets[assets[i].className] = assets[i]; - } - } + addAsset: function (className, symbolProps) { + this.assets[className] = symbolProps; }, resolveTarget: function (target) { if (!target) { @@ -6257,10 +7220,6 @@ function as2ToNumber(value) { case 'undefined': case 'null': return AS2Context.instance.swfVersion >= 7 ? NaN : 0; - default: - return AS2Context.instance.swfVersion >= 5 ? NaN : 0; - case 'null': - return NaN; case 'boolean': return value ? 1 : +0; case 'number': @@ -6270,6 +7229,8 @@ function as2ToNumber(value) { return 0; } return +value; + default: + return AS2Context.instance.swfVersion >= 5 ? NaN : 0; } } function as2ToInteger(value) { @@ -6298,8 +7259,6 @@ function as2ToString(value) { return value.toString(); case 'string': return value; - case 'undefined': - return 'undefined'; case 'movieclip': return value.$targetPath; case 'object': @@ -6396,7 +7355,7 @@ function as2CreatePrototypeProxy(obj) { } }); } -function executeActions(actionsData, context, scope, assets) { +function executeActions(actionsData, context, scope) { var actionTracer = ActionTracerFactory.get(); var scopeContainer = context.initialScope.create(scope); var savedContext = AS2Context.instance; @@ -6404,9 +7363,6 @@ function executeActions(actionsData, context, scope, assets) { AS2Context.instance = context; context.defaultTarget = scope; context.globals.asSetPublicProperty('this', scope); - if (assets) { - context.addAssets(assets); - } actionTracer.message('ActionScript Execution Starts'); actionTracer.indent(); interpretActions(actionsData, scopeContainer, null, []); @@ -7645,10 +8601,9 @@ if (typeof GLOBAL !== 'undefined') { GLOBAL.executeActions = executeActions; GLOBAL.AS2Context = AS2Context; } -var debug = true; var $DEBUG; -var release = false; -var compatibility = true; +var release = true; +var checkArguments = true; var useSurrogates = true; var useAsAdd = true; var sealConstTraits = false; @@ -7656,21 +8611,11 @@ var c4CoerceNonPrimitiveParameters = false; var c4CoerceNonPrimitive = false; var c4AsTypeLate = true; var inBrowser = typeof console != 'undefined'; -if (!inBrowser) { - console = { - info: print, - warn: function (x) { - if (traceWarnings.value) { - print(x); - } - } - }; -} if (!this.performance) { this.performance = {}; } if (!this.performance.now) { - this.performance.now = Date.now; + this.performance.now = dateNow; } function backtrace() { try { @@ -7681,7 +8626,7 @@ function backtrace() { } function error(message) { if (!inBrowser) { - console.info(backtrace()); + console.warn(backtrace()); } throw new Error(message); } @@ -7695,11 +8640,6 @@ function assert(condition) { error(message.join('')); } } -function assertFalse(condition, message) { - if (condition) { - error(message); - } -} function assertNotImplemented(condition, message) { if (!condition) { error('NotImplemented: ' + message); @@ -7708,6 +8648,9 @@ function assertNotImplemented(condition, message) { function warning(message) { true; } +function notUsed(message) { + true; +} function notImplemented(message) { true; } @@ -7896,23 +8839,26 @@ function isNumeric(x) { if (typeof x === 'number') { return x === (x | 0); } - if (typeof x === 'string' && x.length) { - if (x === '0') { - return true; - } - if (x[0] >= '1' && x[0] <= '9') { - for (var i = 1; i < x.length; i++) { - if (!(x[i] >= '1' && x[i] <= '9')) { - return false; - } + if (typeof x !== 'string' || x.length === 0) { + return false; + } + if (x === '0') { + return true; + } + var c = x.charCodeAt(0); + if (c >= 49 && c <= 57) { + for (var i = 1, j = x.length; i < j; i++) { + c = x.charCodeAt(i); + if (!(c >= 48 && c <= 57)) { + return false; } - return true; } + return true; } return false; } function boxValue(value) { - if (isNullOrUndefined(value)) { + if (isNullOrUndefined(value) || isObject(value)) { return value; } return Object(value); @@ -8178,6 +9124,9 @@ function bitCount(i) { function escapeString(str) { if (str !== undefined) { str = str.replace(/[^\w$]/gi, '$'); + if (/^\d/.test(str)) { + str = '$' + str; + } } return str; } @@ -8847,23 +9796,6 @@ var Callback = function () { }; return callback; }(); -function dumpBytes(buffer, start, length) { - var s = ''; - bytes = new Uint8Array(buffer, start, length); - var end = length; - for (var i = 0; i < end; i++) { - if (i % 16 === 0) { - s += '\n' + (start + i) + ': '; - } - s += bytes[i] + ' '; - } - return s; -} -function addProfileMarker(marker) { - if (typeof FirefoxCom !== 'undefined') { - FirefoxCom.requestSync('addProfilerMarker', marker); - } -} var CircularBuffer = function () { var mask = 4095, size = 4096; function circularBuffer(Type) { @@ -8901,12 +9833,25 @@ var CircularBuffer = function () { }; return circularBuffer; }(); +function lazyClass(holder, name, initialize) { + Object.defineProperty(holder, name, { + get: function () { + var start = performance.now(); + var value = initialize(); + print('Initialized Class: ' + name + ' ' + (performance.now() - start).toFixed(4)); + Object.defineProperty(holder, name, { + value: value, + writable: true + }); + return value; + }, + configurable: true + }); +} +function createNewCompartment() { + return newGlobal('new-compartment'); +} (function (exports) { - if (!performance) { - performance = { - now: Date.now - }; - } var Timer = function () { var base = new timer(null, 'Total'), top = base; var flat = new timer(null, 'Flat'), flatStack = []; @@ -8965,7 +9910,7 @@ var CircularBuffer = function () { })); return; } - writer.enter(this.name + ': ' + this.total + ' ms' + ', count: ' + this.count + ', average: ' + (this.total / this.count).toFixed(2) + ' ms'); + writer.enter(this.name + ': ' + this.total.toFixed(2) + ' ms' + ', count: ' + this.count + ', average: ' + (this.total / this.count).toFixed(2) + ' ms'); for (var name in this.timers) { this.timers[name].trace(writer); } @@ -9314,42 +10259,10 @@ var SORT_UNIQUESORT = 4; var SORT_RETURNINDEXEDARRAY = 8; var SORT_NUMERIC = 16; var Errors = { - OutOfMemoryError: { - code: 1000, - message: 'The system is out of memory.' - }, - NotImplementedError: { - code: 1001, - message: 'The method %1 is not implemented.' - }, - InvalidPrecisionError: { - code: 1002, - message: 'Number.toPrecision has a range of 1 to 21. Number.toFixed and Number.toExponential have a range of 0 to 20. Specified value is not within expected range.' - }, - InvalidRadixError: { - code: 1003, - message: 'The radix argument must be between 2 and 36; got %1.' - }, - InvokeOnIncompatibleObjectError: { - code: 1004, - message: 'Method %1 was invoked on an incompatible object.' - }, - ArrayIndexNotIntegerError: { - code: 1005, - message: 'Array index is not a positive integer (%1).' - }, CallOfNonFunctionError: { code: 1006, message: '%1 is not a function.' }, - ConstructOfNonFunctionError: { - code: 1007, - message: 'Instantiation attempted on a non-constructor.' - }, - AmbiguousBindingError: { - code: 1008, - message: '%1 is ambiguous; Found more than one matching binding.' - }, ConvertNullToObjectError: { code: 1009, message: 'Cannot access a property or method of a null object reference.' @@ -9358,434 +10271,22 @@ var Errors = { code: 1010, message: 'A term is undefined and has no properties.' }, - IllegalOpcodeError: { - code: 1011, - message: 'Method %1 contained illegal opcode %2 at offset %3.' - }, - LastInstExceedsCodeSizeError: { - code: 1012, - message: 'The last instruction exceeded code size.' - }, - FindVarWithNoScopeError: { - code: 1013, - message: 'Cannot call OP_findproperty when scopeDepth is 0.' - }, ClassNotFoundError: { code: 1014, message: 'Class %1 could not be found.' }, - IllegalSetDxns: { - code: 1015, - message: 'Method %1 cannot set default xml namespace' - }, - DescendentsError: { - code: 1016, - message: 'Descendants operator (..) not supported on type %1.' - }, - ScopeStackOverflowError: { - code: 1017, - message: 'Scope stack overflow occurred.' - }, - ScopeStackUnderflowError: { - code: 1018, - message: 'Scope stack underflow occurred.' - }, - GetScopeObjectBoundsError: { - code: 1019, - message: 'Getscopeobject %1 is out of bounds.' - }, - CannotFallOffMethodError: { - code: 1020, - message: 'Code cannot fall off the end of a method.' - }, - InvalidBranchTargetError: { - code: 1021, - message: 'At least one branch target was not on a valid instruction in the method.' - }, - IllegalVoidError: { - code: 1022, - message: 'Type void may only be used as a function return type.' - }, - StackOverflowError: { - code: 1023, - message: 'Stack overflow occurred.' - }, - StackUnderflowError: { - code: 1024, - message: 'Stack underflow occurred.' - }, - InvalidRegisterError: { - code: 1025, - message: 'An invalid register %1 was accessed.' - }, - SlotExceedsCountError: { - code: 1026, - message: 'Slot %1 exceeds slotCount=%2 of %3.' - }, - MethodInfoExceedsCountError: { - code: 1027, - message: 'Method_info %1 exceeds method_count=%2.' - }, - DispIdExceedsCountError: { - code: 1028, - message: 'Disp_id %1 exceeds max_disp_id=%2 of %3.' - }, - DispIdUndefinedError: { - code: 1029, - message: 'Disp_id %1 is undefined on %2.' - }, - StackDepthUnbalancedError: { - code: 1030, - message: 'Stack depth is unbalanced. %1 != %2.' - }, - ScopeDepthUnbalancedError: { - code: 1031, - message: 'Scope depth is unbalanced. %1 != %2.' - }, - CpoolIndexRangeError: { - code: 1032, - message: 'Cpool index %1 is out of range %2.' - }, - CpoolEntryWrongTypeError: { - code: 1033, - message: 'Cpool entry %1 is wrong type.' - }, CheckTypeFailedError: { code: 1034, message: 'Type Coercion failed: cannot convert %1 to %2.' }, - IllegalSuperCallError: { - code: 1035, - message: 'Illegal super expression found in method %1.' - }, - CannotAssignToMethodError: { - code: 1037, - message: 'Cannot assign to a method %1 on %2.' - }, - RedefinedError: { - code: 1038, - message: '%1 is already defined.' - }, - CannotVerifyUntilReferencedError: { - code: 1039, - message: 'Cannot verify method until it is referenced.' - }, - CantUseInstanceofOnNonObjectError: { - code: 1040, - message: 'The right-hand side of instanceof must be a class or function.' - }, - IsTypeMustBeClassError: { - code: 1041, - message: 'The right-hand side of operator must be a class.' - }, - InvalidMagicError: { - code: 1042, - message: 'Not an ABC file. major_version=%1 minor_version=%2.' - }, - InvalidCodeLengthError: { - code: 1043, - message: 'Invalid code_length=%1.' - }, - InvalidMethodInfoFlagsError: { - code: 1044, - message: 'MethodInfo-%1 unsupported flags=%2.' - }, - UnsupportedTraitsKindError: { - code: 1045, - message: 'Unsupported traits kind=%1.' - }, - MethodInfoOrderError: { - code: 1046, - message: 'MethodInfo-%1 referenced before definition.' - }, - MissingEntryPointError: { - code: 1047, - message: 'No entry point was found.' - }, - PrototypeTypeError: { - code: 1049, - message: 'Prototype objects must be vanilla Objects.' - }, - ConvertToPrimitiveError: { - code: 1050, - message: 'Cannot convert %1 to primitive.' - }, - IllegalEarlyBindingError: { - code: 1051, - message: 'Illegal early binding access to %1.' - }, - InvalidURIError: { - code: 1052, - message: 'Invalid URI passed to %1 function.' - }, - IllegalOverrideError: { - code: 1053, - message: 'Illegal override of %1 in %2.' - }, - IllegalExceptionHandlerError: { - code: 1054, - message: 'Illegal range or target offsets in exception handler.' - }, - WriteSealedError: { - code: 1056, - message: 'Cannot create property %1 on %2.' - }, - IllegalSlotError: { - code: 1057, - message: '%1 can only contain methods.' - }, - IllegalOperandTypeError: { - code: 1058, - message: 'Illegal operand type: %1 must be %2.' - }, - ClassInfoOrderError: { - code: 1059, - message: 'ClassInfo-%1 is referenced before definition.' - }, - ClassInfoExceedsCountError: { - code: 1060, - message: 'ClassInfo %1 exceeds class_count=%2.' - }, - NumberOutOfRangeError: { - code: 1061, - message: 'The value %1 cannot be converted to %2 without losing precision.' - }, WrongArgumentCountError: { code: 1063, message: 'Argument count mismatch on %1. Expected %2, got %3.' }, - CannotCallMethodAsConstructor: { - code: 1064, - message: 'Cannot call method %1 as constructor.' - }, - UndefinedVarError: { - code: 1065, - message: 'Variable %1 is not defined.' - }, - FunctionConstructorError: { - code: 1066, - message: 'The form function(\'function body\') is not supported.' - }, - IllegalNativeMethodBodyError: { - code: 1067, - message: 'Native method %1 has illegal method body.' - }, - CannotMergeTypesError: { - code: 1068, - message: '%1 and %2 cannot be reconciled.' - }, - ReadSealedError: { - code: 1069, - message: 'Property %1 not found on %2 and there is no default value.' - }, - CallNotFoundError: { - code: 1070, - message: 'Method %1 not found on %2' - }, - AlreadyBoundError: { - code: 1071, - message: 'Function %1 has already been bound to %2.' - }, - ZeroDispIdError: { - code: 1072, - message: 'Disp_id 0 is illegal.' - }, - DuplicateDispIdError: { - code: 1073, - message: 'Non-override method %1 replaced because of duplicate disp_id %2.' - }, - ConstWriteError: { - code: 1074, - message: 'Illegal write to read-only property %1 on %2.' - }, - MathNotFunctionError: { - code: 1075, - message: 'Math is not a function.' - }, - MathNotConstructorError: { - code: 1076, - message: 'Math is not a constructor.' - }, - WriteOnlyError: { - code: 1077, - message: 'Illegal read of write-only property %1 on %2.' - }, - IllegalOpMultinameError: { - code: 1078, - message: 'Illegal opcode/multiname combination: %1<%2>.' - }, - IllegalNativeMethodError: { - code: 1079, - message: 'Native methods are not allowed in loaded code.' - }, - IllegalNamespaceError: { - code: 1080, - message: 'Illegal value for namespace.' - }, - ReadSealedErrorNs: { - code: 1081, - message: 'Property %1 not found on %2 and there is no default value.' - }, - NoDefaultNamespaceError: { - code: 1082, - message: 'No default namespace has been set.' - }, - XMLPrefixNotBound: { - code: 1083, - message: 'The prefix "%1" for element "%2" is not bound.' - }, - XMLBadQName: { - code: 1084, - message: 'Element or attribute ("%1") does not match QName production: QName::=(NCName\':\')?NCName.' - }, - XMLUnterminatedElementTag: { - code: 1085, - message: 'The element type "%1" must be terminated by the matching end-tag "".' - }, - XMLOnlyWorksWithOneItemLists: { - code: 1086, - message: 'The %1 method only works on lists containing one item.' - }, - XMLAssignmentToIndexedXMLNotAllowed: { - code: 1087, - message: 'Assignment to indexed XML is not allowed.' - }, XMLMarkupMustBeWellFormed: { code: 1088, message: 'The markup in the document following the root element must be well-formed.' }, - XMLAssigmentOneItemLists: { - code: 1089, - message: 'Assignment to lists with more than one item is not supported.' - }, - XMLMalformedElement: { - code: 1090, - message: 'XML parser failure: element is malformed.' - }, - XMLUnterminatedCData: { - code: 1091, - message: 'XML parser failure: Unterminated CDATA section.' - }, - XMLUnterminatedXMLDecl: { - code: 1092, - message: 'XML parser failure: Unterminated XML declaration.' - }, - XMLUnterminatedDocTypeDecl: { - code: 1093, - message: 'XML parser failure: Unterminated DOCTYPE declaration.' - }, - XMLUnterminatedComment: { - code: 1094, - message: 'XML parser failure: Unterminated comment.' - }, - XMLUnterminatedAttribute: { - code: 1095, - message: 'XML parser failure: Unterminated attribute.' - }, - XMLUnterminatedElement: { - code: 1096, - message: 'XML parser failure: Unterminated element.' - }, - XMLUnterminatedProcessingInstruction: { - code: 1097, - message: 'XML parser failure: Unterminated processing instruction.' - }, - XMLNamespaceWithPrefixAndNoURI: { - code: 1098, - message: 'Illegal prefix %1 for no namespace.' - }, - RegExpFlagsArgumentError: { - code: 1100, - message: 'Cannot supply flags when constructing one RegExp from another.' - }, - NoScopeError: { - code: 1101, - message: 'Cannot verify method %1 with unknown scope.' - }, - IllegalDefaultValue: { - code: 1102, - message: 'Illegal default value for type %1.' - }, - CannotExtendFinalClass: { - code: 1103, - message: 'Class %1 cannot extend final base class.' - }, - XMLDuplicateAttribute: { - code: 1104, - message: 'Attribute "%1" was already specified for element "%2".' - }, - CorruptABCError: { - code: 1107, - message: 'The ABC data is corrupt, attempt to read out of bounds.' - }, - InvalidBaseClassError: { - code: 1108, - message: 'The OP_newclass opcode was used with the incorrect base class.' - }, - DanglingFunctionError: { - code: 1109, - message: 'Attempt to directly call unbound function %1 from method %2.' - }, - CannotExtendError: { - code: 1110, - message: '%1 cannot extend %2.' - }, - CannotImplementError: { - code: 1111, - message: '%1 cannot implement %2.' - }, - CoerceArgumentCountError: { - code: 1112, - message: 'Argument count mismatch on class coercion. Expected 1, got %1.' - }, - InvalidNewActivationError: { - code: 1113, - message: 'OP_newactivation used in method without NEED_ACTIVATION flag.' - }, - NoGlobalScopeError: { - code: 1114, - message: 'OP_getglobalslot or OP_setglobalslot used with no global scope.' - }, - NotConstructorError: { - code: 1115, - message: '%1 is not a constructor.' - }, - ApplyError: { - code: 1116, - message: 'second argument to Function.prototype.apply must be an array.' - }, - XMLInvalidName: { - code: 1117, - message: 'Invalid XML name: %1.' - }, - XMLIllegalCyclicalLoop: { - code: 1118, - message: 'Illegal cyclical loop between nodes.' - }, - DeleteTypeError: { - code: 1119, - message: 'Delete operator is not supported with operand of type %1.' - }, - DeleteSealedError: { - code: 1120, - message: 'Cannot delete property %1 on %2.' - }, - DuplicateMethodBodyError: { - code: 1121, - message: 'Method %1 has a duplicate method body.' - }, - IllegalInterfaceMethodBodyError: { - code: 1122, - message: 'Interface method %1 has illegal method body.' - }, - FilterError: { - code: 1123, - message: 'Filter operator not supported on type %1.' - }, - InvalidHasNextError: { - code: 1124, - message: 'OP_hasnext2 requires object and index to be distinct registers.' - }, OutOfRangeError: { code: 1125, message: 'The index %1 is out of range %2.' @@ -9794,66 +10295,6 @@ var Errors = { code: 1126, message: 'Cannot change the length of a fixed Vector.' }, - TypeAppOfNonParamType: { - code: 1127, - message: 'Type application attempted on a non-parameterized type.' - }, - WrongTypeArgCountError: { - code: 1128, - message: 'Incorrect number of type parameters for %1. Expected %2, got %3.' - }, - JSONCyclicStructure: { - code: 1129, - message: 'Cyclic structure cannot be converted to JSON string.' - }, - JSONInvalidReplacer: { - code: 1131, - message: 'Replacer argument to JSON stringifier must be an array or a two parameter function.' - }, - JSONInvalidParseInput: { - code: 1132, - message: 'Invalid JSON parse input.' - }, - FileOpenError: { - code: 1500, - message: 'Error occurred opening file %1.' - }, - FileWriteError: { - code: 1501, - message: 'Error occurred writing to file %1.' - }, - ScriptTimeoutError: { - code: 1502, - message: 'A script has executed for longer than the default timeout period of 15 seconds.' - }, - ScriptTerminatedError: { - code: 1503, - message: 'A script failed to exit after 30 seconds and was terminated.' - }, - EndOfFileError: { - code: 1504, - message: 'End of file.' - }, - StringIndexOutOfBoundsError: { - code: 1505, - message: 'The string index %1 is out of bounds; must be in range %2 to %3.' - }, - InvalidRangeError: { - code: 1506, - message: 'The specified range is invalid.' - }, - NullArgumentError: { - code: 1507, - message: 'Argument %1 cannot be null.' - }, - InvalidArgumentError: { - code: 1508, - message: 'The value specified for argument %1 is invalid.' - }, - ArrayFilterNonNullObjectError: { - code: 1510, - message: 'When the callback argument is a method of a class, the optional this argument must be null.' - }, InvalidParamError: { code: 2004, message: 'One of the parameters is invalid.' @@ -9870,142 +10311,10 @@ var Errors = { code: 2008, message: 'Parameter %1 must be one of the accepted values.' }, - CantInstantiateError: { - code: 2012, - message: '%1 class cannot be instantiated.' - }, ArgumentError: { code: 2015, message: 'Invalid BitmapData.' }, - EOFError: { - code: 2030, - message: 'End of file was encountered.' - }, - CompressedDataError: { - code: 2058, - message: 'There was an error decompressing the data.' - }, - EmptyStringError: { - code: 2085, - message: 'Parameter %1 must be non-empty string.' - }, - ProxyGetPropertyError: { - code: 2088, - message: 'The Proxy class does not implement getProperty. It must be overridden by a subclass.' - }, - ProxySetPropertyError: { - code: 2089, - message: 'The Proxy class does not implement setProperty. It must be overridden by a subclass.' - }, - ProxyCallPropertyError: { - code: 2090, - message: 'The Proxy class does not implement callProperty. It must be overridden by a subclass.' - }, - ProxyHasPropertyError: { - code: 2091, - message: 'The Proxy class does not implement hasProperty. It must be overridden by a subclass.' - }, - ProxyDeletePropertyError: { - code: 2092, - message: 'The Proxy class does not implement deleteProperty. It must be overridden by a subclass.' - }, - ProxyGetDescendantsError: { - code: 2093, - message: 'The Proxy class does not implement getDescendants. It must be overridden by a subclass.' - }, - ProxyNextNameIndexError: { - code: 2105, - message: 'The Proxy class does not implement nextNameIndex. It must be overridden by a subclass.' - }, - ProxyNextNameError: { - code: 2106, - message: 'The Proxy class does not implement nextName. It must be overridden by a subclass.' - }, - ProxyNextValueError: { - code: 2107, - message: 'The Proxy class does not implement nextValue. It must be overridden by a subclass.' - }, - InvalidArrayLengthError: { - code: 2108, - message: 'The value %1 is not a valid Array length.' - }, - ReadExternalNotImplementedError: { - code: 2173, - message: 'Unable to read object in stream. The class %1 does not implement flash.utils.IExternalizable but is aliased to an externalizable class.' - }, - NoSecurityContextError: { - code: 2000, - message: 'No active security context.' - }, - TooFewArgumentsError: { - code: 2001, - message: 'Too few arguments were specified; got %1, %2 expected.' - }, - InvalidSocketError: { - code: 2002, - message: 'Operation attempted on invalid socket.' - }, - InvalidSocketPortError: { - code: 2003, - message: 'Invalid socket port number specified.' - }, - ParamTypeError: { - code: 2005, - message: 'Parameter %1 is of the incorrect type. Should be type %2.' - }, - HasStyleSheetError: { - code: 2009, - message: 'This method cannot be used on a text field with a style sheet.' - }, - SocketLocalFileSecurityError: { - code: 2010, - message: 'Local-with-filesystem SWF files are not permitted to use sockets.' - }, - SocketConnectError: { - code: 2011, - message: 'Socket connection failed to %1:%2.' - }, - AuthoringOnlyFeatureError: { - code: 2013, - message: 'Feature can only be used in Flash Authoring.' - }, - FeatureNotAvailableError: { - code: 2014, - message: 'Feature is not available at this time.' - }, - InvalidBitmapDataError: { - code: 2015, - message: 'Invalid BitmapData.' - }, - SystemExitSecurityError: { - code: 2017, - message: 'Only trusted local files may cause the Flash Player to exit.' - }, - SystemExitUnsupportedError: { - code: 2018, - message: 'System.exit is only available in the standalone Flash Player.' - }, - InvalidDepthError: { - code: 2019, - message: 'Depth specified is invalid.' - }, - MovieClipSwapError: { - code: 2020, - message: 'MovieClips objects with different parents cannot be swapped.' - }, - ObjectCreationError: { - code: 2021, - message: 'Object creation failed.' - }, - NotDisplayObjectError: { - code: 2022, - message: 'Class %1 must inherit from DisplayObject to link to a symbol.' - }, - NotSpriteError: { - code: 2023, - message: 'Class %1 must inherit from Sprite to link to the root.' - }, CantAddSelfError: { code: 2024, message: 'An object cannot be added as a child of itself.' @@ -10013,1678 +10322,6 @@ var Errors = { NotAChildError: { code: 2025, message: 'The supplied DisplayObject must be a child of the caller.' - }, - NavigateURLError: { - code: 2026, - message: 'An error occurred navigating to the URL %1.' - }, - MustBeNonNegativeError: { - code: 2027, - message: 'Parameter %1 must be a non-negative number; got %2.' - }, - LocalSecurityError: { - code: 2028, - message: 'Local-with-filesystem SWF file %1 cannot access Internet URL %2.' - }, - InvalidStreamError: { - code: 2029, - message: 'This URLStream object does not have a stream opened.' - }, - SocketError: { - code: 2031, - message: 'Socket Error.' - }, - StreamError: { - code: 2032, - message: 'Stream Error.' - }, - KeyGenerationError: { - code: 2033, - message: 'Key Generation Failed.' - }, - InvalidKeyError: { - code: 2034, - message: 'An invalid digest was supplied.' - }, - URLNotFoundError: { - code: 2035, - message: 'URL Not Found.' - }, - LoadNeverCompletedError: { - code: 2036, - message: 'Load Never Completed.' - }, - InvalidCallError: { - code: 2037, - message: 'Functions called in incorrect sequence, or earlier call was unsuccessful.' - }, - FileIOError: { - code: 2038, - message: 'File I/O Error.' - }, - RemoteURLError: { - code: 2039, - message: 'Invalid remote URL protocol. The remote URL protocol must be HTTP or HTTPS.' - }, - BrowseInProgressError: { - code: 2041, - message: 'Only one file browsing session may be performed at a time.' - }, - DigestNotSupportedError: { - code: 2042, - message: 'The digest property is not supported by this load operation.' - }, - UnhandledError: { - code: 2044, - message: 'Unhandled %1:.' - }, - FileVerificationError: { - code: 2046, - message: 'The loaded file did not have a valid signature.' - }, - DisplayListSecurityError: { - code: 2047, - message: 'Security sandbox violation: %1: %2 cannot access %3.' - }, - DownloadSecurityError: { - code: 2048, - message: 'Security sandbox violation: %1 cannot load data from %2.' - }, - UploadSecurityError: { - code: 2049, - message: 'Security sandbox violation: %1 cannot upload data to %2.' - }, - OutboundScriptingSecurityError: { - code: 2051, - message: 'Security sandbox violation: %1 cannot evaluate scripting URLs within %2 (allowScriptAccess is %3). Attempted URL was %4.' - }, - AllowDomainArgumentError: { - code: 2052, - message: 'Only String arguments are permitted for allowDomain and allowInsecureDomain.' - }, - IntervalSecurityError: { - code: 2053, - message: 'Security sandbox violation: %1 cannot clear an interval timer set by %2.' - }, - ExactSettingsError: { - code: 2054, - message: 'The value of Security.exactSettings cannot be changed after it has been used.' - }, - PrintJobStartError: { - code: 2055, - message: 'The print job could not be started.' - }, - PrintJobSendError: { - code: 2056, - message: 'The print job could not be sent to the printer.' - }, - PrintJobAddPageError: { - code: 2057, - message: 'The page could not be added to the print job.' - }, - ExternalCallbackSecurityError: { - code: 2059, - message: 'Security sandbox violation: %1 cannot overwrite an ExternalInterface callback added by %2.' - }, - ExternalInterfaceSecurityError: { - code: 2060, - message: 'Security sandbox violation: ExternalInterface caller %1 cannot access %2.' - }, - ExternalInterfaceNoCallbackError: { - code: 2061, - message: 'No ExternalInterface callback %1 registered.' - }, - NoCloneMethodError: { - code: 2062, - message: 'Children of Event must override clone() {return new MyEventClass (...);}.' - }, - IMEError: { - code: 2063, - message: 'Error attempting to execute IME command.' - }, - FocusNotSetError: { - code: 2065, - message: 'The focus cannot be set for this target.' - }, - DelayRangeError: { - code: 2066, - message: 'The Timer delay specified is out of range.' - }, - ExternalInterfaceNotAvailableError: { - code: 2067, - message: 'The ExternalInterface is not available in this container. ExternalInterface requires Internet Explorer ActiveX, Firefox, Mozilla 1.7.5 and greater, or other browsers that support NPRuntime.' - }, - InvalidSoundError: { - code: 2068, - message: 'Invalid sound.' - }, - InvalidLoaderMethodError: { - code: 2069, - message: 'The Loader class does not implement this method.' - }, - StageOwnerSecurityError: { - code: 2070, - message: 'Security sandbox violation: caller %1 cannot access Stage owned by %2.' - }, - InvalidStageMethodError: { - code: 2071, - message: 'The Stage class does not implement this property or method.' - }, - ProductManagerDiskError: { - code: 2073, - message: 'There was a problem saving the application to disk.' - }, - ProductManagerStageError: { - code: 2074, - message: 'The stage is too small to fit the download ui.' - }, - ProductManagerVerifyError: { - code: 2075, - message: 'The downloaded file is invalid.' - }, - FilterFailedError: { - code: 2077, - message: 'This filter operation cannot be performed with the specified input parameters.' - }, - TimelineObjectNameSealedError: { - code: 2078, - message: 'The name property of a Timeline-placed object cannot be modified.' - }, - BitmapNotAssociatedWithBitsCharError: { - code: 2079, - message: 'Classes derived from Bitmap can only be associated with defineBits characters (bitmaps).' - }, - AlreadyConnectedError: { - code: 2082, - message: 'Connect failed because the object is already connected.' - }, - CloseNotConnectedError: { - code: 2083, - message: 'Close failed because the object is not connected.' - }, - ArgumentSizeError: { - code: 2084, - message: 'The AMF encoding of the arguments cannot exceed 40K.' - }, - FileReferenceProhibitedError: { - code: 2086, - message: 'A setting in the mms.cfg file prohibits this FileReference request.' - }, - DownloadFileNameProhibitedError: { - code: 2087, - message: 'The FileReference.download() file name contains prohibited characters.' - }, - EventDispatchRecursionError: { - code: 2094, - message: 'Event dispatch recursion overflow.' - }, - AsyncError: { - code: 2095, - message: '%1 was unable to invoke callback %2.' - }, - DisallowedHTTPHeaderError: { - code: 2096, - message: 'The HTTP request header %1 cannot be set via ActionScript.' - }, - FileFilterError: { - code: 2097, - message: 'The FileFilter Array is not in the correct format.' - }, - LoadingObjectNotSWFError: { - code: 2098, - message: 'The loading object is not a .swf file, you cannot request SWF properties from it.' - }, - LoadingObjectNotInitializedError: { - code: 2099, - message: 'The loading object is not sufficiently loaded to provide this information.' - }, - EmptyByteArrayError: { - code: 2100, - message: 'The ByteArray parameter in Loader.loadBytes() must have length greater than 0.' - }, - DecodeParamError: { - code: 2101, - message: 'The String passed to URLVariables.decode() must be a URL-encoded query string containing name/value pairs.' - }, - NotAnXMLChildError: { - code: 2102, - message: 'The before XMLNode parameter must be a child of the caller.' - }, - XMLRecursionError: { - code: 2103, - message: 'XML recursion failure: new child would create infinite loop.' - }, - SceneNotFoundError: { - code: 2108, - message: 'Scene %1 was not found.' - }, - FrameLabelNotFoundError: { - code: 2109, - message: 'Frame label %1 not found in scene %2.' - }, - DisableAVM1LoadingError: { - code: 2110, - message: 'The value of Security.disableAVM1Loading cannot be set unless the caller can access the stage and is in an ActionScript 3.0 SWF file.' - }, - AVM1LoadingError: { - code: 2111, - message: 'Security.disableAVM1Loading is true so the current load of the ActionScript 1.0/2.0 SWF file has been blocked.' - }, - ApplicationDomainSecurityError: { - code: 2112, - message: 'Provided parameter LoaderContext.ApplicationDomain is from a disallowed domain.' - }, - SecurityDomainSecurityError: { - code: 2113, - message: 'Provided parameter LoaderContext.SecurityDomain is from a disallowed domain.' - }, - NonNullPointerError: { - code: 2114, - message: 'Parameter %1 must be null.' - }, - TrueParamError: { - code: 2115, - message: 'Parameter %1 must be false.' - }, - FalseParamError: { - code: 2116, - message: 'Parameter %1 must be true.' - }, - InvalidLoaderInfoMethodError: { - code: 2118, - message: 'The LoaderInfo class does not implement this method.' - }, - LoaderInfoAppDomainSecurityError: { - code: 2119, - message: 'Security sandbox violation: caller %1 cannot access LoaderInfo.applicationDomain owned by %2.' - }, - SecuritySwfNotAllowedError: { - code: 2121, - message: 'Security sandbox violation: %1: %2 cannot access %3. This may be worked around by calling Security.allowDomain.' - }, - SecurityNonSwfIncompletePolicyFilesError: { - code: 2122, - message: 'Security sandbox violation: %1: %2 cannot access %3. A policy file is required, but the checkPolicyFile flag was not set when this media was loaded.' - }, - SecurityNonSwfNotAllowedError: { - code: 2123, - message: 'Security sandbox violation: %1: %2 cannot access %3. No policy files granted access.' - }, - UnknownFileTypeError: { - code: 2124, - message: 'Loaded file is an unknown type.' - }, - SecurityCrossVMNotAllowedError: { - code: 2125, - message: 'Security sandbox violation: %1 cannot use Runtime Shared Library %2 because crossing the boundary between ActionScript 3.0 and ActionScript 1.0/2.0 objects is not allowed.' - }, - NotConnectedError: { - code: 2126, - message: 'NetConnection object must be connected.' - }, - FileRefBadPostDataTypeError: { - code: 2127, - message: 'FileReference POST data cannot be type ByteArray.' - }, - NetConnectionConnectError: { - code: 2129, - message: 'Connection to %1 failed.' - }, - SharedObjectFlushFailedError: { - code: 2130, - message: 'Unable to flush SharedObject.' - }, - DefinitionNotFoundError: { - code: 2131, - message: 'Definition %1 cannot be found.' - }, - NetConnectionInvalidConnectFromNetStatusEventError: { - code: 2132, - message: 'NetConnection.connect cannot be called from a netStatus event handler.' - }, - CallbackNotRegisteredError: { - code: 2133, - message: 'Callback %1 is not registered.' - }, - SharedObjectCreateError: { - code: 2134, - message: 'Cannot create SharedObject.' - }, - InvalidSWFError: { - code: 2136, - message: 'The SWF file %1 contains invalid data.' - }, - NavigationSecurityError: { - code: 2137, - message: 'Security sandbox violation: %1 cannot navigate window %2 within %3 (allowScriptAccess is %4). Attempted URL was %5.' - }, - NonParsableRichTextXMLError: { - code: 2138, - message: 'Rich text XML could not be parsed.' - }, - SharedObjectConnectError: { - code: 2139, - message: 'SharedObject could not connect.' - }, - LocalSecurityLoadingError: { - code: 2140, - message: 'Security sandbox violation: %1 cannot load %2. Local-with-filesystem and local-with-networking SWF files cannot load each other.' - }, - MultiplePrintJobsError: { - code: 2141, - message: 'Only one PrintJob may be in use at a time.' - }, - LocalImportSecurityError: { - code: 2142, - message: 'Security sandbox violation: local SWF files cannot use the LoaderContext.securityDomain property. %1 was attempting to load %2.' - }, - AccOverrideRole: { - code: 2143, - message: 'AccessibilityImplementation.get_accRole() must be overridden from its default.' - }, - AccOverrideState: { - code: 2144, - message: 'AccessibilityImplementation.get_accState() must be overridden from its default.' - }, - URLRequestHeaderInvalidLengthError: { - code: 2145, - message: 'Cumulative length of requestHeaders must be less than 8192 characters.' - }, - AllowNetworkingSecurityError: { - code: 2146, - message: 'Security sandbox violation: %1 cannot call %2 because the HTML/container parameter allowNetworking has the value %3.' - }, - ForbiddenProtocolError: { - code: 2147, - message: 'Forbidden protocol in URL %1.' - }, - RemoteToLocalSecurityError: { - code: 2148, - message: 'SWF file %1 cannot access local resource %2. Only local-with-filesystem and trusted local SWF files may access local resources.' - }, - FsCommandSecurityError: { - code: 2149, - message: 'Security sandbox violation: %1 cannot make fscommand calls to %2 (allowScriptAccess is %3).' - }, - CantAddParentError: { - code: 2150, - message: 'An object cannot be added as a child to one of it\'s children (or children\'s children, etc.).' - }, - FullScreenSecurityError: { - code: 2151, - message: 'You cannot enter full screen mode when the settings dialog is visible.' - }, - FullScreenNotAllowedError: { - code: 2152, - message: 'Full screen mode is not allowed.' - }, - URLRequestInvalidHeader: { - code: 2153, - message: 'The URLRequest.requestHeaders array must contain only non-NULL URLRequestHeader objects.' - }, - InvalidNetStreamObject: { - code: 2154, - message: 'The NetStream Object is invalid. This may be due to a failed NetConnection.' - }, - InvalidFunctionName: { - code: 2155, - message: 'The ExternalInterface.call functionName parameter is invalid. Only alphanumeric characters are supported.' - }, - ForbiddenPortForProtocolError: { - code: 2156, - message: 'Port %1 may not be accessed using protocol %2. Calling SWF was %3.' - }, - NoAsfunctionErrror: { - code: 2157, - message: 'Rejecting URL %1 because the \'asfunction:\' protocol may only be used for link targets, not for networking APIs.' - }, - InvalidNetConnectionObject: { - code: 2158, - message: 'The NetConnection Object is invalid. This may be due to a dropped NetConnection.' - }, - InvalidSharedObject: { - code: 2159, - message: 'The SharedObject Object is invalid.' - }, - InvalidTextLineError: { - code: 2160, - message: 'The TextLine is INVALID and cannot be used to access the current state of the TextBlock.' - }, - TextLayoutError: { - code: 2161, - message: 'An internal error occured while laying out the text.' - }, - FragmentOutputType: { - code: 2162, - message: 'The Shader output type is not compatible for this operation.' - }, - FragmentInputType: { - code: 2163, - message: 'The Shader input type %1 is not compatible for this operation.' - }, - FragmentInputMissing: { - code: 2164, - message: 'The Shader input %1 is missing or an unsupported type.' - }, - FragmentInputTooSmall: { - code: 2165, - message: 'The Shader input %1 does not have enough data.' - }, - FragmentInputNoDimension: { - code: 2166, - message: 'The Shader input %1 lacks valid dimensions.' - }, - FragmentNotEnoughInput: { - code: 2167, - message: 'The Shader does not have the required number of inputs for this operation.' - }, - StaticTextLineError: { - code: 2168, - message: 'Static text lines have no atoms and no reference to a text block.' - }, - SecurityQuestionableBrowserScriptingError: { - code: 2169, - message: 'The method %1 may not be used for browser scripting. The URL %2 requested by %3 is being ignored. If you intend to call browser script, use navigateToURL instead.' - }, - HeaderSecurityError: { - code: 2170, - message: 'Security sandbox violation: %1 cannot send HTTP headers to %2.' - }, - FragmentMissing: { - code: 2171, - message: 'The Shader object contains no byte code to execute.' - }, - FragmentAlreadyRunning: { - code: 2172, - message: 'The ShaderJob is already running or finished.' - }, - FileReferenceBusyError: { - code: 2174, - message: 'Only one download, upload, load or save operation can be active at a time on each FileReference.' - }, - UnformattedElementError: { - code: 2175, - message: 'One or more elements of the content of the TextBlock has a null ElementFormat.' - }, - UserActionRequiredError: { - code: 2176, - message: 'Certain actions, such as those that display a pop-up window, may only be invoked upon user interaction, for example by a mouse click or button press.' - }, - FragmentInputTooLarge: { - code: 2177, - message: 'The Shader input %1 is too large.' - }, - ClipboardConstNotAllowed: { - code: 2178, - message: 'The Clipboard.generalClipboard object must be used instead of creating a new Clipboard.' - }, - ClipboardDisallowedRead: { - code: 2179, - message: 'The Clipboard.generalClipboard object may only be read while processing a flash.events.Event.PASTE event.' - }, - CantMoveAVM1ContentLoadedIntoAVM2: { - code: 2180, - message: 'It is illegal to move AVM1 content (AS1 or AS2) to a different part of the displayList when it has been loaded into AVM2 (AS3) content.' - }, - InvalidTextLineMethodError: { - code: 2181, - message: 'The TextLine class does not implement this property or method.' - }, - PerspectiveFieldOfViewValueInvalid: { - code: 2182, - message: 'Invalid fieldOfView value. The value must be greater than 0 and less than 180.' - }, - Invalid3DScale: { - code: 2183, - message: 'Scale values must not be zero.' - }, - LockedElementFormatError: { - code: 2184, - message: 'The ElementFormat object is locked and cannot be modified.' - }, - LockedFontDescriptionError: { - code: 2185, - message: 'The FontDescription object is locked and cannot be modified.' - }, - PerspectiveFocalLengthInvalid: { - code: 2186, - message: 'Invalid focalLength %1.' - }, - Matrix3DDecomposeTypeInvalid: { - code: 2187, - message: 'Invalid orientation style %1. Value must be one of \'Orientation3D.EULER_ANGLES\', \'Orientation3D.AXIS_ANGLE\', or \'Orientation3D.QUATERNION\'.' - }, - MatrixNonInvertibleError: { - code: 2188, - message: 'Invalid raw matrix. Matrix must be invertible.' - }, - Matrix3DRefCannontBeShared: { - code: 2189, - message: 'A Matrix3D can not be assigned to more than one DisplayObject.' - }, - ForceDownloadSecurityError: { - code: 2190, - message: 'The attempted load of %1 failed as it had a Content-Disposition of attachment set.' - }, - ClipboardDisallowedWrite: { - code: 2191, - message: 'The Clipboard.generalClipboard object may only be written to as the result of user interaction, for example by a mouse click or button press.' - }, - MalformedUnicodeError: { - code: 2192, - message: 'An unpaired Unicode surrogate was encountered in the input.' - }, - SecurityContentAccessDeniedError: { - code: 2193, - message: 'Security sandbox violation: %1: %2 cannot access %3.' - }, - LoaderParamError: { - code: 2194, - message: 'Parameter %1 cannot be a Loader.' - }, - LoaderAsyncError: { - code: 2195, - message: 'Error thrown as Loader called %1.' - }, - ObjectWithStringsParamError: { - code: 2196, - message: 'Parameter %1 must be an Object with only String values.' - }, - SystemUpdaterPlayerNotSupportedError: { - code: 2200, - message: 'The SystemUpdater class is not supported by this player.' - }, - SystemUpdaterOSNotSupportedError: { - code: 2201, - message: 'The requested update type is not supported on this operating system.' - }, - SystemUpdaterBusy: { - code: 2202, - message: 'Only one SystemUpdater action is allowed at a time.' - }, - SystemUpdaterFailed: { - code: 2203, - message: 'The requested SystemUpdater action cannot be completed.' - }, - SystemUpdaterCannotCancel: { - code: 2204, - message: 'This operation cannot be canceled because it is waiting for user interaction.' - }, - SystemUpdaterUnknownTarget: { - code: 2205, - message: 'Invalid update type %1.' - }, - SignedSWfLoadingError: { - code: 2500, - message: 'An error occurred decrypting the signed swf file. The swf will not be loaded.' - }, - NotScreenSharingError: { - code: 2501, - message: 'This property can only be accessed during screen sharing.' - }, - NotSharingMonitorError: { - code: 2502, - message: 'This property can only be accessed if sharing the entire screen.' - }, - FileBadPathName: { - code: 3000, - message: 'Illegal path name.' - }, - FileAccessDenied: { - code: 3001, - message: 'File or directory access denied.' - }, - FileExists: { - code: 3002, - message: 'File or directory exists.' - }, - FileDoesNotExist: { - code: 3003, - message: 'File or directory does not exist.' - }, - FileInsufficientSpace: { - code: 3004, - message: 'Insufficient file space.' - }, - FileSystemResources: { - code: 3005, - message: 'Insufficient system resources.' - }, - FileNotAFile: { - code: 3006, - message: 'Not a file.' - }, - FileNotADir: { - code: 3007, - message: 'Not a directory.' - }, - FileReadOnlyFileSys: { - code: 3008, - message: 'Read-only or write-protected media.' - }, - FileNotSameDevice: { - code: 3009, - message: 'Cannot move file or directory to a different device.' - }, - DirNotEmpty: { - code: 3010, - message: 'Directory is not empty.' - }, - FileDestinationExists: { - code: 3011, - message: 'Move or copy destination already exists.' - }, - FileCantDelete: { - code: 3012, - message: 'Cannot delete file or directory.' - }, - FileInUse: { - code: 3013, - message: 'File or directory is in use.' - }, - FileCopyMoveAncestor: { - code: 3014, - message: 'Cannot copy or move a file or directory to overwrite a containing directory.' - }, - LoadBytesCodeExecutionSecurityError: { - code: 3015, - message: 'Loader.loadBytes() is not permitted to load content with executable code.' - }, - FileApplicationNotFound: { - code: 3016, - message: 'No application was found that can open this file.' - }, - SQLConnectionCannotClose: { - code: 3100, - message: 'A SQLConnection cannot be closed while statements are still executing.' - }, - SQLConnectionAlreadyOpen: { - code: 3101, - message: 'Database connection is already open.' - }, - SQLConnectionInvalidName: { - code: 3102, - message: 'Name argument specified was invalid. It must not be null or empty.' - }, - SQLConnectionInTransaction: { - code: 3103, - message: 'Operation cannot be performed while there is an open transaction on this connection.' - }, - SQLConnectionNotOpen: { - code: 3104, - message: 'A SQLConnection must be open to perform this operation.' - }, - SQLConnectionNoOpenTransaction: { - code: 3105, - message: 'Operation is only allowed if a connection has an open transaction.' - }, - SQLStatementIsExecutingProperty: { - code: 3106, - message: 'Property cannot be changed while SQLStatement.executing is true.' - }, - SQLStatementIvalidCall: { - code: 3107, - message: '%1 may not be called unless SQLResult.complete is false.' - }, - SQLStatementInvalidText: { - code: 3108, - message: 'Operation is not permitted when the SQLStatement.text property is not set.' - }, - SQLStatementInvalidConnection: { - code: 3109, - message: 'Operation is not permitted when the SQLStatement.sqlConnection property is not set.' - }, - SQLStatementIsExecutingCall: { - code: 3110, - message: 'Operation cannot be performed while SQLStatement.executing is true.' - }, - SQLStatementInvalidSchemaType: { - code: 3111, - message: 'An invalid schema type was specified.' - }, - SQLConnectionInvalidLockType: { - code: 3112, - message: 'An invalid transaction lock type was specified.' - }, - SQLConnectionNotFileReference: { - code: 3113, - message: 'Reference specified is not of type File.' - }, - SQLConnectionInvalidModeSpecified: { - code: 3114, - message: 'An invalid open mode was specified.' - }, - SQLGeneralEngineError: { - code: 3115, - message: 'SQL Error.' - }, - SQLInternalEngineError: { - code: 3116, - message: 'An internal logic error occurred.' - }, - SQLPermissionError: { - code: 3117, - message: 'Access permission denied.' - }, - SQLOperationAbortedError: { - code: 3118, - message: 'Operation aborted.' - }, - SQLDatabaseLockedError: { - code: 3119, - message: 'Database file is currently locked.' - }, - SQLTableLockedError: { - code: 3120, - message: 'Table is locked.' - }, - SQLOutOfMemoryError: { - code: 3121, - message: 'Out of memory.' - }, - SQLDatabaseIsReadonlyError: { - code: 3122, - message: 'Attempt to write a readonly database.' - }, - SQLDatabaseCorruptError: { - code: 3123, - message: 'Database disk image is malformed.' - }, - SQLDatabaseFullError: { - code: 3124, - message: 'Insertion failed because database is full.' - }, - SQLCannotOpenDatabaseError: { - code: 3125, - message: 'Unable to open the database file.' - }, - SQLLockingProtocolError: { - code: 3126, - message: 'Database lock protocol error.' - }, - SQLDatabaseEmptyError: { - code: 3127, - message: 'Database is empty.' - }, - SQLDiskIOError: { - code: 3128, - message: 'Disk I/O error occurred.' - }, - SQLSchemaChangedError: { - code: 3129, - message: 'The database schema changed.' - }, - SQLTooMuchDataError: { - code: 3130, - message: 'Too much data for one row of a table.' - }, - SQLConstraintError: { - code: 3131, - message: 'Abort due to constraint violation.' - }, - SQLDataTypeMismatchError: { - code: 3132, - message: 'Data type mismatch.' - }, - SQLConcurrencyError: { - code: 3133, - message: 'An internal error occurred.' - }, - SQLNotSupportedOnOSError: { - code: 3134, - message: 'Feature not supported on this operating system.' - }, - SQLAuthorizationDeniedError: { - code: 3135, - message: 'Authorization denied.' - }, - SQLAuxDatabaseFormatError: { - code: 3136, - message: 'Auxiliary database format error.' - }, - SQLBindingRangeError: { - code: 3137, - message: 'An index specified for a parameter was out of range.' - }, - SQLInvalidDatabaseFileError: { - code: 3138, - message: 'File opened is not a database file.' - }, - SQLInvalidPageSizeError: { - code: 3139, - message: 'The page size specified was not valid for this operation.' - }, - SQLInvalidKeySizeError: { - code: 3140, - message: 'The encryption key size specified was not valid for this operation. Keys must be exactly 16 bytes in length' - }, - SQLInvalidConfigurationError: { - code: 3141, - message: 'The requested database configuration is not supported.' - }, - SQLCannotRekeyNonKeyedDatabase: { - code: 3143, - message: 'Unencrypted databases may not be reencrypted.' - }, - NativeWindowClosedError: { - code: 3200, - message: 'Cannot perform operation on closed window.' - }, - PDFNoReaderInstalled: { - code: 3201, - message: 'Adobe Reader cannot be found.' - }, - PDFOldReaderInstalled: { - code: 3202, - message: 'Adobe Reader 8.1 or later cannot be found.' - }, - PDFOldDefaultText: { - code: 3203, - message: 'Default Adobe Reader must be version 8.1 or later.' - }, - PDFCannotLoadReader: { - code: 3204, - message: 'An error ocurred trying to load Adobe Reader.' - }, - ApplicationFeatureSecurityError: { - code: 3205, - message: 'Only application-sandbox content can access this feature.' - }, - LoaderInfoDoorSecurityError: { - code: 3206, - message: 'Caller %1 cannot set LoaderInfo property %2.' - }, - ApplicationNonFeatureSecurityError: { - code: 3207, - message: 'Application-sandbox content cannot access this feature.' - }, - InvalidClipboardAccess: { - code: 3208, - message: 'Attempt to access invalid clipboard.' - }, - DeadClipboardAccess: { - code: 3209, - message: 'Attempt to access dead clipboard.' - }, - DeadJavaScriptObjectAccess: { - code: 3210, - message: 'The application attempted to reference a JavaScript object in a HTML page that is no longer loaded.' - }, - FilePromiseIOError: { - code: 3211, - message: 'Drag and Drop File Promise error: %1' - }, - NativeProcessNotRunning: { - code: 3212, - message: 'Cannot perform operation on a NativeProcess that is not running.' - }, - NativeProcessAlreadyRunning: { - code: 3213, - message: 'Cannot perform operation on a NativeProcess that is already running.' - }, - NativeProcessBadExecutable: { - code: 3214, - message: 'NativeProcessStartupInfo.executable does not specify a valid executable file.' - }, - NativeProcessBadWorkingDirectory: { - code: 3215, - message: 'NativeProcessStartupInfo.workingDirectory does not specify a valid directory.' - }, - NativeProcessStdOutReadError: { - code: 3216, - message: 'Error while reading data from NativeProcess.standardOutput.' - }, - NativeProcessStdErrReadError: { - code: 3217, - message: 'Error while reading data from NativeProcess.standardError.' - }, - NativeProcessStdInWriteError: { - code: 3218, - message: 'Error while writing data to NativeProcess.standardInput.' - }, - NativeProcessNotStarted: { - code: 3219, - message: 'The NativeProcess could not be started. \'%1\'' - }, - ActionNotAllowedSecurityError: { - code: 3220, - message: 'Action \'%1\' not allowed in current security context \'%2\'.' - }, - SWFNoPlayerInstalled: { - code: 3221, - message: 'Adobe Flash Player cannot be found.' - }, - SWFOldPlayerInstalled: { - code: 3222, - message: 'The installed version of Adobe Flash Player is too old.' - }, - DNSResolverLookupError: { - code: 3223, - message: 'DNS lookup error: platform error %1' - }, - SocketMessageTooLongError: { - code: 3224, - message: 'Socket message too long' - }, - SocketCannotSendDataToAddressAfterConnect: { - code: 3225, - message: 'Cannot send data to a location when connected.' - }, - AllowCodeImportError: { - code: 3226, - message: 'Cannot import a SWF file when LoaderContext.allowCodeImport is false.' - }, - BackgroundLaunchError: { - code: 3227, - message: 'Cannot launch another application from background.' - }, - StageWebViewLoadError: { - code: 3228, - message: 'StageWebView encountered an error during the load operation.' - }, - StageWebViewProtocolNotSupported: { - code: 3229, - message: 'The protocol is not supported.:' - }, - BrowseOperationUnsupported: { - code: 3230, - message: 'The browse operation is unsupported.' - }, - InvalidVoucher: { - code: 3300, - message: 'Voucher is invalid.' - }, - AuthenticationFailed: { - code: 3301, - message: 'User authentication failed.' - }, - RequireSSLError: { - code: 3302, - message: 'Flash Access server does not support SSL.' - }, - ContentExpiredError: { - code: 3303, - message: 'Content expired.' - }, - AuthorizationFailed: { - code: 3304, - message: 'User authorization failed (for example, the user has not purchased the content).' - }, - ServerConnectionFailed: { - code: 3305, - message: 'Can\'t connect to the server.' - }, - ClientUpdateRequired: { - code: 3306, - message: 'Client update required (Flash Access server requires new client).' - }, - InternalError: { - code: 3307, - message: 'Generic internal Flash Access failure.' - }, - WrongVoucherKey: { - code: 3308, - message: 'Wrong voucher key.' - }, - CorruptedFLV: { - code: 3309, - message: 'Video content is corrupted.' - }, - AppIDMismatch: { - code: 3310, - message: 'The AIR application or Flash Player SWF does not match the one specified in the DRM policy.' - }, - AppVersionMismatch: { - code: 3311, - message: 'The version of the application does not match the one specified in the DRM policy.' - }, - VoucherIntegrityError: { - code: 3312, - message: 'Verification of voucher failed.' - }, - WriteFileSystemFailed: { - code: 3313, - message: 'Write to the file system failed.' - }, - FLVHeaderIntegrityFailed: { - code: 3314, - message: 'Verification of FLV/F4V header file failed.' - }, - PermissionDenied: { - code: 3315, - message: 'The current security context does not allow this operation.' - }, - LocalConnectionUserScopedLocked: { - code: 3316, - message: 'The value of LocalConnection.isPerUser cannot be changed because it has already been locked by a call to LocalConnection.connect, .send, or .close.' - }, - LoadAdobeCPFailed: { - code: 3317, - message: 'Failed to load Flash Access module.' - }, - IncompatibleAdobeCPVersion: { - code: 3318, - message: 'Incompatible version of Flash Access module found.' - }, - MissingAdobeCPEntryPoint: { - code: 3319, - message: 'Missing Flash Access module API entry point.' - }, - InternalErrorHA: { - code: 3320, - message: 'Generic internal Flash Access failure.' - }, - IndividualizationFailed: { - code: 3321, - message: 'Individualization failed.' - }, - DeviceBindingFailed: { - code: 3322, - message: 'Device binding failed.' - }, - CorruptStore: { - code: 3323, - message: 'The internal stores are corrupted.' - }, - MachineTokenInvalid: { - code: 3324, - message: 'Reset license files and the client will fetch a new machine token.' - }, - CorruptServerStateStore: { - code: 3325, - message: 'Internal stores are corrupt.' - }, - TamperingDetected: { - code: 3326, - message: 'Call customer support.' - }, - ClockTamperingDetected: { - code: 3327, - message: 'Clock tampering detected.' - }, - ServerErrorTryAgain: { - code: 3328, - message: 'Server error; retry the request.' - }, - ApplicationSpecificError: { - code: 3329, - message: 'Error in application-specific namespace.' - }, - NeedAuthentication: { - code: 3330, - message: 'Need to authenticate the user and reacquire the voucher.' - }, - ContentNotYetValid: { - code: 3331, - message: 'Content is not yet valid.' - }, - CachedVoucherExpired: { - code: 3332, - message: 'Cached voucher has expired. Reacquire the voucher from the server.' - }, - PlaybackWindowExpired: { - code: 3333, - message: 'The playback window for this policy has expired.' - }, - InvalidDRMPlatform: { - code: 3334, - message: 'This platform is not allowed to play this content.' - }, - InvalidDRMVersion: { - code: 3335, - message: 'Invalid version of Flash Access module. Upgrade AIR or Flash Access module for the Flash Player.' - }, - InvalidRuntimePlatform: { - code: 3336, - message: 'This platform is not allowed to play this content.' - }, - InvalidRuntimeVersion: { - code: 3337, - message: 'Upgrade Flash Player or AIR and retry playback.' - }, - UnknownConnectionType: { - code: 3338, - message: 'Unknown connection type.' - }, - NoAnalogPlaybackAllowed: { - code: 3339, - message: 'Can\'t play back on analog device. Connect to a digital device.' - }, - NoAnalogProtectionAvail: { - code: 3340, - message: 'Can\'t play back because connected analog device doesn\'t have the correct capabilities.' - }, - NoDigitalPlaybackAllowed: { - code: 3341, - message: 'Can\'t play back on digital device.' - }, - NoDigitalProtectionAvail: { - code: 3342, - message: 'The connected digital device doesn\'t have the correct capabilities.' - }, - InternalErrorIV: { - code: 3343, - message: 'Internal Error.' - }, - MissingAdobeCPModule: { - code: 3344, - message: 'Missing Flash Access module.' - }, - DRMNoAccessError: { - code: 3345, - message: 'This operation is not permitted with content protected using Flash Access.' - }, - DRMDataMigrationFailed: { - code: 3346, - message: 'Failed migrating local DRM data, all locally cached DRM vouchers are lost.' - }, - DRMInsufficientDeviceCapabilites: { - code: 3347, - message: 'The device does not meet the Flash Access server\'s playback device constraints.' - }, - DRMHardStopIntervalExpired: { - code: 3348, - message: 'This protected content is expired.' - }, - DRMServerVersionTooHigh: { - code: 3349, - message: 'The Flash Access server is running at a version that\'s higher than the max supported by this runtime.' - }, - DRMServerVersionTooLow: { - code: 3350, - message: 'The Flash Access server is running at a version that\'s lower than the min supported by this runtime.' - }, - DRMDeviceGroupTokenInvalid: { - code: 3351, - message: 'Device Group registration token is corrupted, please refresh the token by registering again to the DRMDeviceGroup.' - }, - DRMDeviceGroupTokenTooOld: { - code: 3352, - message: 'The server is using a newer version of the registration token for this Device Group. Please refresh the token by registering again to the DRMDeviceGroup.' - }, - DRMDeviceGroupTokenTooNew: { - code: 3353, - message: 'the server is using an older version of the registration token for this Device Group.' - }, - DRMDeviceGroupTokenExpired: { - code: 3354, - message: 'Device Group registration is expired, please refresh the token by registering again to the DRMDeviceGroup.' - }, - JoinDRMDeviceGroupFailed: { - code: 3355, - message: 'The server denied this Device Group registration request.' - }, - DRMVoucherHasNoCorrespondingRoot: { - code: 3356, - message: 'The root voucher for this content\'s DRMVoucher was not found.' - }, - NoValidEmbeddedDRMVoucher: { - code: 3357, - message: 'The DRMContentData provides no valid embedded voucher and no Flash Access server url to acquire the voucher from.' - }, - NoACPProtectionAvailable: { - code: 3358, - message: 'ACP protection is not available on the device but required to playback the content.' - }, - NoCGMSAProtectionAvailable: { - code: 3359, - message: 'CGMSA protection is not available on the device but required to playback the content.' - }, - DRMDeviceGroupRegistrationRequired: { - code: 3360, - message: 'Device Group registration is required before doing this operation.' - }, - DeviceIsNotRegisteredToDRMDeviceGroup: { - code: 3361, - message: 'The device is not registered to this Device Group.' - }, - ScriptBridgeError: { - code: 3400, - message: 'An error occured while executing JavaScript code.' - }, - ScriptBridgeNameAccessSecurityError: { - code: 3401, - message: 'Security sandbox violation: An object with this name has already been registered from another security domain.' - }, - ScriptBridgeBrowserAccessSecurityError: { - code: 3402, - message: 'Security sandbox violation: Bridge caller %1 cannot access %2.' - }, - ExtensionContextNoSuchMethod: { - code: 3500, - message: 'The extension context does not have a method with the name %1.' - }, - ExtensionContextAlreadyDisposed: { - code: 3501, - message: 'The extension context has already been disposed.' - }, - ExtensionContextInvalidReturnValue: { - code: 3502, - message: 'The extension returned an invalid value.' - }, - ExtensionContextInvalidState: { - code: 3503, - message: 'The extension was left in an invalid state.' - }, - NoValidProgramSet: { - code: 3600, - message: 'No valid program set.' - }, - NoValidIndexBufferSet: { - code: 3601, - message: 'No valid index buffer set.' - }, - SanityCheckOnParametersFailed: { - code: 3602, - message: 'Sanity check on parameters failed, %1 triangles and %2 index offset.' - }, - NotEnoughIndicesInThisBuffer: { - code: 3603, - message: 'Not enough indices in this buffer. %1 triangles at offset %2, but there are only %3 indices in buffer.' - }, - SampleBindsTextureAlsoBoundToRender: { - code: 3604, - message: 'Sampler %1 binds a texture that is also bound for render to texture.' - }, - SampleBindsInvalidTexture: { - code: 3605, - message: 'Sampler %1 binds an invalid texture.' - }, - SamplerFormatDoesNotMatchTextureFormat: { - code: 3606, - message: 'Sampler %1 format does not match texture format.' - }, - StreamIsNotUsed: { - code: 3607, - message: 'Stream %1 is set but not used by the current vertex program.' - }, - StreamIsInvalid: { - code: 3608, - message: 'Stream %1 is invalid.' - }, - StreamDoesNotHaveEnoughVertices: { - code: 3609, - message: 'Stream %1 does not have enough vertices.' - }, - StreamVertexOffsetOutOfBounds: { - code: 3610, - message: 'Stream %1 vertex offset is out of bounds' - }, - StreamReadButNotSet: { - code: 3611, - message: 'Stream %1 is read by the current vertex program but not set.' - }, - ProgramMustBeLittleEndian: { - code: 3612, - message: 'Programs must be in little endian format.' - }, - NativeShaderCompilationFailed: { - code: 3613, - message: 'The native shader compilation failed.' - }, - NativeShaderCompilationFailedOpenGL: { - code: 3614, - message: 'The native shader compilation failed.enGL specific: %1' - }, - AgalProgramTooSmall: { - code: 3615, - message: 'AGAL validation failed: Program size below minimum length for %1 program.' - }, - NotAnAgalProgram: { - code: 3616, - message: 'AGAL validation failed: Not an AGAL program. Wrong magic byte for %1 program.' - }, - BadAgalVersion: { - code: 3617, - message: 'AGAL validation failed: Bad AGAL version for %1 program. Current version is %2.' - }, - BadAgalProgramType: { - code: 3618, - message: 'AGAL validation failed: Bad AGAL program type identifier for %1 program.' - }, - BadAgalShadertype: { - code: 3619, - message: 'AGAL validation failed: Shader type must be either fragment or vertex for %1 program.' - }, - InvalidAgalOpcodeOutOfRange: { - code: 3620, - message: 'AGAL validation failed: Invalid opcode, value out of range: %2 at token %3 of %1 program.' - }, - InvalidAgalOpcodeNotImplemented: { - code: 3621, - message: 'AGAL validation failed: Invalid opcode, %2 is not implemented in this version at token %3 of %1 program.' - }, - AgalOpcodeOnlyAllowedInFragmentProgram: { - code: 3622, - message: 'AGAL validation failed: Opcode %2 only allowed in fragment programs at token %3 of %1 program.' - }, - OpenConditionNesting: { - code: 3623, - message: 'AGAL validation failed: Open condition nesting (close without open) at token %2 of %1 program.' - }, - ConditionNestingTooDeep: { - code: 3624, - message: 'AGAL validation failed: Condition nesting (%2) too deep at token %3 of %1 program.' - }, - BadAgalSourceOperands: { - code: 3625, - message: 'AGAL validation failed: Bad AGAL source operands. Both are constants (this must be precomputed) at token %2 of %1 program.' - }, - BothOperandsAreIndirectReads: { - code: 3626, - message: 'AGAL validation failed: Opcode %2, both operands are indirect reads at token %3 of %1 program.' - }, - OpcodeDestinationMustBeAllZero: { - code: 3627, - message: 'AGAL validation failed: Opcode %2 destination operand must be all zero at token %3 of %1 program.' - }, - OpcodeDestinationMustUseMask: { - code: 3628, - message: 'AGAL validation failed: The destination operand for the %2 instruction must mask w (use .xyz or less) at token %3 of %1 program.' - }, - TooManyTokens: { - code: 3629, - message: 'AGAL validation failed: Too many tokens (%2) for %1 program.' - }, - FragmentShaderType: { - code: 3630, - message: 'Fragment shader type is not fragment.' - }, - VertexShaderType: { - code: 3631, - message: 'Vertex shader type is not vertex.' - }, - VaryingReadButNotWrittenTo: { - code: 3632, - message: 'AGAL linkage: Varying %1 is read in the fragment shader but not written to by the vertex shader.' - }, - VaryingPartialWrite: { - code: 3633, - message: 'AGAL linkage: Varying %1 is only partially written to. Must write all four components.' - }, - FragmentWriteAllComponents: { - code: 3634, - message: 'AGAL linkage: Fragment output needs to write to all components.' - }, - VertexWriteAllComponents: { - code: 3635, - message: 'AGAL linkage: Vertex output needs to write to all components.' - }, - UnusedOperand: { - code: 3636, - message: 'AGAL validation failed: Unused operand is not set to zero for %2 at token %3 of %1 program.' - }, - SamplerRegisterOnlyInFragment: { - code: 3637, - message: 'AGAL validation failed: Sampler registers only allowed in fragment programs for %2 at token %3 of %1 program.' - }, - SamplerRegisterSecondOperand: { - code: 3638, - message: 'AGAL validation failed: Sampler register only allowed as second operand in texture instructions for %2 at token %3 of %1 program.' - }, - IndirectOnlyAllowedInVertex: { - code: 3639, - message: 'AGAL validation failed: Indirect addressing only allowed in vertex programs for %2 at token %3 of %1 program.' - }, - IndirectOnlyIntoConstantRegisters: { - code: 3640, - message: 'AGAL validation failed: Indirect addressing only allowed into constant registers for %2 at token %3 of %1 program.' - }, - IndirectNotAllowed: { - code: 3641, - message: 'AGAL validation failed: Indirect addressing not allowed for this operand in this instruction for %2 at token %3 of %1 program.' - }, - IndirectSourceType: { - code: 3642, - message: 'AGAL validation failed: Indirect source type must be attribute, constant or temporary for %2 at token %3 of %1 program.' - }, - IndirectAddressingFieldsMustBeZero: { - code: 3643, - message: 'AGAL validation failed: Indirect addressing fields must be zero for direct adressing for %2 at token %3 of %1 program.' - }, - VaryingRegistersOnlyReadInFragment: { - code: 3644, - message: 'AGAL validation failed: Varying registers can only be read in fragment programs for %2 at token %3 of %1 program.' - }, - AttributeRegistersOnlyReadInVertex: { - code: 3645, - message: 'AGAL validation failed: Attribute registers can only be read in vertex programs for %2 at token %3 of %1 program.' - }, - CanNotReadOutputRegister: { - code: 3646, - message: 'AGAL validation failed: Can not read from output register for %2 at token %3 of %1 program.' - }, - TempRegisterReadWithoutWrite: { - code: 3647, - message: 'AGAL validation failed: Temporary register read without being written to for %2 at token %3 of %1 program.' - }, - TempRegisterComponentReadWithoutWrite: { - code: 3648, - message: 'AGAL validation failed: Temporary register component read without being written to for %2 at token %3 of %1 program.' - }, - SamplerRegisterCannotBeWrittenTo: { - code: 3649, - message: 'AGAL validation failed: Sampler registers can not be written to for %2 at token %3 of %1 program.' - }, - VaryingRegistersWrite: { - code: 3650, - message: 'AGAL validation failed: Varying registers can only be written in vertex programs for %2 at token %3 of %1 program.' - }, - AttributeRegisterCannotBeWrittenTo: { - code: 3651, - message: 'AGAL validation failed: Attribute registers can not be written to for %2 at token %3 of %1 program.' - }, - ConstantRegisterCannotBeWrittenTo: { - code: 3652, - message: 'AGAL validation failed: Constant registers can not be written to for %2 at token %3 of %1 program.' - }, - DestinationWritemaskIsZero: { - code: 3653, - message: 'AGAL validation failed: Destination writemask is zero for %2 at token %3 of %1 program.' - }, - AGALReservedBitsShouldBeZero: { - code: 3654, - message: 'AGAL validation failed: Reserve bits should be zero for %2 at token %3 of %1 program.' - }, - UnknownRegisterType: { - code: 3655, - message: 'AGAL validation failed: Unknown register type for %2 at token %3 of %1 program.' - }, - SamplerRegisterOutOfBounds: { - code: 3656, - message: 'AGAL validation failed: Sampler register index out of bounds for %2 at token %3 of %1 program.' - }, - VaryingRegisterOutOfBounds: { - code: 3657, - message: 'AGAL validation failed: Varying register index out of bounds for %2 at token %3 of %1 program.' - }, - AttributeRegisterOutOfBounds: { - code: 3658, - message: 'AGAL validation failed: Attribute register index out of bounds for %2 at token %3 of %1 program.' - }, - ConstantRegisterOutOfBounds: { - code: 3659, - message: 'AGAL validation failed: Constant register index out of bounds for %2 at token %3 of %1 program.' - }, - OutputRegisterOutOfBounds: { - code: 3660, - message: 'AGAL validation failed: Output register index out of bounds for %2 at token %3 of %1 program.' - }, - TemporaryRegisterOutOfBounds: { - code: 3661, - message: 'AGAL validation failed: Temporary register index out of bounds for %2 at token %3 of %1 program.' - }, - CubeMapSamplerMustUseClamp: { - code: 3662, - message: 'AGAL validation failed: Cube map samplers must set wrapping to clamp mode for %2 at token %3 of %1 program.' - }, - SampleBindsUndefinedTexture: { - code: 3663, - message: 'Sampler %1 binds an undefined texture.' - }, - UnknownSamplerDimension: { - code: 3664, - message: 'AGAL validation failed: Unknown sampler dimension %4 for %2 at token %3 of %1 program.' - }, - UnknownFilterMode: { - code: 3665, - message: 'AGAL validation failed: Unknown filter mode in sampler: %4 for %2 at token %3 of %1 program.' - }, - UnknownMipmapMode: { - code: 3666, - message: 'AGAL validation failed: Unknown mipmap mode in sampler: %4 for %2 at token %3 of %1 program.' - }, - UnknownWrappingMode: { - code: 3667, - message: 'AGAL validation failed: Unknown wrapping mode in sampler: %4 for %2 at token %3 of %1 program.' - }, - UnknownSpecialFlag: { - code: 3668, - message: 'AGAL validation failed: Unknown special flag used in sampler: %4 for %2 at token %3 of %1 program.' - }, - BadInputSize: { - code: 3669, - message: 'Bad input size.' - }, - BufferTooBig: { - code: 3670, - message: 'Buffer too big.' - }, - BufferHasZeroSize: { - code: 3671, - message: 'Buffer has zero size.' - }, - BufferCreationFailed: { - code: 3672, - message: 'Buffer creation failed. Internal error.' - }, - InvalidCubeSide: { - code: 3673, - message: 'Cube side must be [0..5].' - }, - MiplevelTooLarge: { - code: 3674, - message: 'Miplevel too large.' - }, - TextureFormatMismatch: { - code: 3675, - message: 'Texture format mismatch.' - }, - PlatformDoesNotSupportTextureFormat: { - code: 3676, - message: 'Platform does not support desired texture format.' - }, - TextureDecodingFailed: { - code: 3677, - message: 'Texture decoding failed. Internal error.' - }, - TextureNeedsToBeSquare: { - code: 3678, - message: 'Texture needs to be square.' - }, - TextureSizeDoesNotMatch: { - code: 3679, - message: 'Texture size does not match.' - }, - DepthTextureNotImplemented: { - code: 3680, - message: 'Depth texture not implemented yet.' - }, - TextureSizeIsZero: { - code: 3681, - message: 'Texture size is zero.' - }, - TextureNotPowerOfTwo: { - code: 3682, - message: 'Texture size not a power of two.' - }, - TextureTooBig: { - code: 3683, - message: 'Texture too big (max is 2048x2048).' - }, - TextureCreationFailed: { - code: 3684, - message: 'Texture creation failed. Internal error.' - }, - CouldNotCreateRenderer: { - code: 3685, - message: 'Could not create renderer.' - }, - DisabledFormatNeedsNullVertexBuffer: { - code: 3686, - message: '\'disabled\' format only valid with a null vertex buffer.' - }, - NullVertexBufferNeedsDisabledFormat: { - code: 3687, - message: 'Null vertex buffers require the \'disabled\' format.' - }, - NeedListenerToRequestContext: { - code: 3688, - message: 'You must add an event listener for the context3DCreate event before requesting a new Context3D.' - }, - CantSwizzle2ndSource: { - code: 3689, - message: 'You can not swizzle second operand for %2 at token %3 of %1 program.' - }, - TooManyDrawCalls: { - code: 3690, - message: 'Too many draw calls before calling present.' - }, - ResourceLimitExceeded: { - code: 3691, - message: 'Resource limit for this resource type exceeded.' - }, - NeedToClearBeforeDraw: { - code: 3692, - message: 'All buffers need to be cleared every frame before drawing.' - }, - SecondOperandMustBeSamplerRegister: { - code: 3693, - message: 'AGAL validation failed: Sampler register must be used for second operand in texture instructions for %2 at token %3 of %1 program.' - }, - ObjectDisposed: { - code: 3694, - message: 'The object was disposed by an earlier call of dispose() on it.' - }, - SameTextureNeedsSameSamplerParams: { - code: 3695, - message: 'A texture can only be bound to multiple samplers if the samplers also have the exact same properties. Mismatch at samplers %1 and %2.' - }, - SecondUseOfSamplerMustHaveSameParams: { - code: 3696, - message: 'AGAL validation failed: Second use of sampler register needs to specify the exact same properties. At token %3 of %1 program.' - }, - TextureBoundButNotUsed: { - code: 3697, - message: 'A texture is bound on sampler %1 but not used by the fragment program.' - }, - BackBufferNotConfigured: { - code: 3698, - message: 'The back buffer is not configured.' - }, - OperationFailed: { - code: 3699, - message: 'Requested Operation failed to complete' - }, - TextureMipchainIsNotComplete: { - code: 3700, - message: 'A texture sampler binds an incomplete texture. Make sure to upload(). All miplevels are required when mipmapping is enabled.' - }, - OutputColorNotMaskable: { - code: 3701, - message: 'The output color register can not use a write mask. All components must be written.' - }, - Context3DNotAvailable: { - code: 3702, - message: 'Context3D not available.' - }, - SwizzleMustBeScalar: { - code: 3703, - message: 'AGAL validation failed: Source swizzle must be scalar (one of: xxxx, yyyy, zzzz, wwww) for %2 at token %3 of %1 program.' - }, - CubeMapSamplerMustUseMipmap: { - code: 3704, - message: 'AGAL validation failed: Cube map samplers must enable mipmapping for %2 at token %3 of %1 program.' } }; function getErrorMessage(index) { @@ -12874,22 +11511,22 @@ var opcodeTable = [ null ]; (function processOpcodeTable() { - function splitter(value) { - var list = value.split(':'); - return { - name: list[0], - size: list[1].substring(0, 3), - type: list[1].substring(3) - }; - } for (var i = 0; i < opcodeTable.length; i++) { var entry = opcodeTable[i]; if (entry && entry.operands !== null) { - if (entry.operands === '') { - entry.operands = []; - } else { - entry.operands = entry.operands.split(',').map(splitter); + var result = []; + if (entry.operands !== '') { + var operands = entry.operands.split(','); + for (var j = 0; j < operands.length; j++) { + var list = operands[j].split(':'); + result.push({ + name: list[0], + size: list[1].substring(0, 3), + type: list[1].substring(3) + }); + } } + entry.operands = result; } } }()); @@ -13355,7 +11992,7 @@ var Multiname = function () { multiname.parse = function parse(constantPool, stream, multinames, patchFactoryTypes) { var index = 0; var kind = stream.readU8(); - var name, namespaces = [], flags = 0, typeParameter; + var name, namespaces = [], flags = 0; switch (kind) { case CONSTANT_QName: case CONSTANT_QNameA: @@ -13424,7 +12061,6 @@ var Multiname = function () { }); } return mn; - break; default: unexpected(); break; @@ -13708,6 +12344,7 @@ var ConstantPool = function constantPool() { for (i = 1; i < n; ++i) { doubles.push(stream.readDouble()); } + Timer.start('Parse Strings'); var strings = [ '' ]; @@ -13716,10 +12353,12 @@ var ConstantPool = function constantPool() { strings.push(stream.readUTFString(stream.readU30())); } this.positionAfterUTFStrings = stream.pos; + Timer.stop(); this.ints = ints; this.uints = uints; this.doubles = doubles; this.strings = strings; + Timer.start('Parse Namespaces'); var namespaces = [ undefined ]; @@ -13729,6 +12368,8 @@ var ConstantPool = function constantPool() { namespace.parse(this, stream); namespaces.push(namespace); } + Timer.stop(); + Timer.start('Parse Namespace Sets'); var namespaceSets = [ undefined ]; @@ -13742,8 +12383,10 @@ var ConstantPool = function constantPool() { } namespaceSets.push(set); } + Timer.stop(); this.namespaces = namespaces; this.namespaceSets = namespaceSets; + Timer.start('Parse Multinames'); var multinames = [ undefined ]; @@ -13752,12 +12395,7 @@ var ConstantPool = function constantPool() { for (i = 1; i < n; ++i) { multinames.push(Multiname.parse(this, stream, multinames, patchFactoryTypes)); } - patchFactoryTypes.forEach(function (patch) { - var multiname = multinames[patch.index]; - true; - patch.multiname.name = multiname.name; - patch.multiname.namespaces = multiname.namespaces; - }); + Timer.stop(); this.multinames = multinames; } constantPool.prototype.getValue = function getValue(kind, index) { @@ -14053,41 +12691,55 @@ var ScriptInfo = function scriptInfo() { }(); var AbcFile = function () { function abcFile(bytes, name) { - console.time('Parse ABC: ' + name); + Timer.start('Parse ABC'); this.name = name; var n, i; var stream = new AbcStream(bytes); checkMagic(stream); + Timer.start('Parse constantPool'); this.constantPool = new ConstantPool(stream, name); + Timer.stop(); + Timer.start('Parse Method Infos'); this.methods = []; n = stream.readU30(); for (i = 0; i < n; ++i) { this.methods.push(new MethodInfo(this, stream)); } + Timer.stop(); + Timer.start('Parse MetaData Infos'); this.metadata = []; n = stream.readU30(); for (i = 0; i < n; ++i) { this.metadata.push(new MetaDataInfo(this, stream)); } + Timer.stop(); + Timer.start('Parse Instance Infos'); this.instances = []; n = stream.readU30(); for (i = 0; i < n; ++i) { this.instances.push(new InstanceInfo(this, stream)); } + Timer.stop(); + Timer.start('Parse Class Infos'); this.classes = []; for (i = 0; i < n; ++i) { this.classes.push(new ClassInfo(this, this.instances[i], stream)); } + Timer.stop(); + Timer.start('Parse Script Infos'); this.scripts = []; n = stream.readU30(); for (i = 0; i < n; ++i) { this.scripts.push(new ScriptInfo(this, i, stream)); } + Timer.stop(); + Timer.start('Parse Method Body Info'); n = stream.readU30(); for (i = 0; i < n; ++i) { MethodInfo.parseBody(this, stream); } - console.timeEnd('Parse ABC: ' + name); + Timer.stop(); + Timer.stop(); } function checkMagic(stream) { var magic = stream.readWord(); @@ -17386,6 +16038,9 @@ var TraitsType = function () { if (mn instanceof MultinameType) { return null; } + if (mn.isAttribute()) { + return null; + } if (followSuperType && (this.isInstanceInfo() || this.isClassInfo())) { var that = this; do { @@ -17614,7 +16269,7 @@ var Verifier = function () { function verification(methodInfo, scope) { this.scope = scope; this.methodInfo = methodInfo; - this.domain = methodInfo.abc.domain; + this.domain = methodInfo.abc.applicationDomain; this.writer = new IndentingWriter(); this.returnType = Type.Undefined; } @@ -17702,7 +16357,7 @@ var Verifier = function () { var scope = state.scope; var writer = verifierTraceLevel.value ? this.writer : null; var bytecodes = this.methodInfo.analysis.bytecodes; - var domain = this.methodInfo.abc.domain; + var domain = this.methodInfo.abc.applicationDomain; var multinames = this.methodInfo.abc.constantPool.multinames; var mi = this.methodInfo; var bc, obj, fn, mn, l, r, val, type, returnType; @@ -17754,7 +16409,9 @@ var Verifier = function () { } var trait = classType.getTrait(mn, false, true); if (trait) { - ti().object = LazyInitializer.create(classType.object); + if (!mi.isInstanceInitializer) { + ti().object = LazyInitializer.create(classType.object); + } return classType; } } @@ -17876,6 +16533,7 @@ var Verifier = function () { mn = popMultiname(); obj = pop(); true; + ti().baseClass = LazyInitializer.create(this.thisType.super().classType().object); push(getProperty(obj.super(), mn)); break; case 5: @@ -17883,6 +16541,7 @@ var Verifier = function () { mn = popMultiname(); obj = pop(); true; + ti().baseClass = LazyInitializer.create(this.thisType.super().classType().object); setProperty(obj.super(), mn, val); break; case 6: @@ -18032,20 +16691,19 @@ var Verifier = function () { notImplemented(bc); break; case 69: - stack.popMany(bc.argCount); - mn = popMultiname(); - obj = pop(); - getProperty(obj, mn); - push(Type.Any); - break; + case 78: case 79: case 70: case 76: stack.popMany(bc.argCount); mn = popMultiname(); obj = pop(); + if (op === OP_callsuper || op === OP_callsupervoid) { + obj = this.thisType.super(); + ti().baseClass = LazyInitializer.create(this.thisType.super().classType().object); + } type = getProperty(obj, mn); - if (op === OP_callpropvoid) { + if (op === OP_callpropvoid || op === OP_callsupervoid) { break; } if (type instanceof MethodType) { @@ -18091,11 +16749,6 @@ var Verifier = function () { case 77: notImplemented(bc); break; - case 78: - stack.popMany(bc.argCount); - popMultiname(); - pop(); - break; case 80: case 81: case 82: @@ -18156,11 +16809,7 @@ var Verifier = function () { local[bc.index] = pop(); break; case 100: - if (savedScope.object) { - push(Type.from(savedScope.global.object)); - } else { - push(Type.Any); - } + push(Type.from(savedScope.global.object)); break; case 101: push(scope[bc.index]); @@ -18514,6 +17163,11 @@ var Verifier = function () { assert: '', internal: true } + }, + ASCallSuper: { + scope: { + assert: '' + } } }, New: { @@ -18541,7 +17195,12 @@ var Verifier = function () { }, ASGetDescendants: {}, ASHasProperty: {}, - ASGetSlot: {} + ASGetSlot: {}, + ASGetSuper: { + scope: { + assert: '' + } + } }, SetProperty: { object: { @@ -18558,7 +17217,12 @@ var Verifier = function () { internal: true } }, - ASSetSlot: {} + ASSetSlot: {}, + ASSetSuper: { + scope: { + assert: '' + } + } }, DeleteProperty: { object: { @@ -18799,7 +17463,7 @@ var Verifier = function () { writer.writeLn('this.id = nextID[nextID.length - 1] += 1;'); writer.leave('}'); if (path.length > 1) { - writer.writeLn(prototypeName + ' = ' + 'extend(' + path[path.length - 2]._name + ', "' + node._name + '")'); + writer.writeLn(prototypeName + ' = ' + 'extend(' + path[path.length - 2]._name + ', "' + node._name + '");'); } writer.writeLn(prototypeName + '.nodeName = "' + node._name + '";'); writer.enter(prototypeName + '.visitInputs = function (visitor) {'); @@ -19096,6 +17760,39 @@ var Verifier = function () { }; return asCallPropertyNode; }(); + var ASCallSuper = function () { + function asCallSuperNode(control, store, object, name, args, flags, scope) { + true; + true; + true; + true; + true; + true; + true; + true; + this.control = control; + this.store = store; + this.loads = undefined; + this.object = object; + this.name = name; + this.args = args; + this.flags = flags; + this.scope = scope; + this.id = nextID[nextID.length - 1] += 1; + } + asCallSuperNode.prototype = extend(CallProperty, 'ASCallSuper'); + asCallSuperNode.prototype.nodeName = 'ASCallSuper'; + asCallSuperNode.prototype.visitInputs = function (visitor) { + this.control && visitor(this.control); + this.store && visitor(this.store); + this.loads && visitArrayInputs(this.loads, visitor); + visitor(this.object); + visitor(this.name); + visitArrayInputs(this.args, visitor); + visitor(this.scope); + }; + return asCallSuperNode; + }(); var New = function () { function newNode(control, store, callee, args) { true; @@ -19273,6 +17970,34 @@ var Verifier = function () { }; return asGetSlotNode; }(); + var ASGetSuper = function () { + function asGetSuperNode(control, store, object, name, scope) { + true; + true; + true; + true; + true; + true; + this.control = control; + this.store = store; + this.loads = undefined; + this.object = object; + this.name = name; + this.scope = scope; + this.id = nextID[nextID.length - 1] += 1; + } + asGetSuperNode.prototype = extend(GetProperty, 'ASGetSuper'); + asGetSuperNode.prototype.nodeName = 'ASGetSuper'; + asGetSuperNode.prototype.visitInputs = function (visitor) { + this.control && visitor(this.control); + this.store && visitor(this.store); + this.loads && visitArrayInputs(this.loads, visitor); + visitor(this.object); + visitor(this.name); + visitor(this.scope); + }; + return asGetSuperNode; + }(); var SetProperty = function () { function setPropertyNode(control, store, object, name, value) { true; @@ -19358,6 +18083,37 @@ var Verifier = function () { }; return asSetSlotNode; }(); + var ASSetSuper = function () { + function asSetSuperNode(control, store, object, name, value, scope) { + true; + true; + true; + true; + true; + true; + true; + this.control = control; + this.store = store; + this.loads = undefined; + this.object = object; + this.name = name; + this.value = value; + this.scope = scope; + this.id = nextID[nextID.length - 1] += 1; + } + asSetSuperNode.prototype = extend(SetProperty, 'ASSetSuper'); + asSetSuperNode.prototype.nodeName = 'ASSetSuper'; + asSetSuperNode.prototype.visitInputs = function (visitor) { + this.control && visitor(this.control); + this.store && visitor(this.store); + this.loads && visitArrayInputs(this.loads, visitor); + visitor(this.object); + visitor(this.name); + visitor(this.value); + visitor(this.scope); + }; + return asSetSuperNode; + }(); var DeleteProperty = function () { function deletePropertyNode(control, store, object, name) { true; @@ -21051,11 +19807,14 @@ var Verifier = function () { exports.SetProperty = SetProperty; exports.CallProperty = CallProperty; exports.ASCallProperty = ASCallProperty; + exports.ASCallSuper = ASCallSuper; exports.ASGetProperty = ASGetProperty; + exports.ASGetSuper = ASGetSuper; exports.ASHasProperty = ASHasProperty; exports.ASDeleteProperty = ASDeleteProperty; exports.ASGetDescendants = ASGetDescendants; exports.ASSetProperty = ASSetProperty; + exports.ASSetSuper = ASSetSuper; exports.ASGetSlot = ASGetSlot; exports.ASSetSlot = ASSetSlot; exports.Call = Call; @@ -21260,8 +20019,8 @@ var createName = function createName(namespaces, name) { node.mustFloat = true; return node; } - function warn(message) { - console.warn(message); + function info(message) { + console.info(message); } function unary(operator, argument) { var node = new Unary(operator, argument); @@ -21403,7 +20162,7 @@ var createName = function createName(namespaces, name) { local = coercer(local); } else if (c4CoerceNonPrimitiveParameters) { local = new Call(start, state.store, globalProperty('asCoerceByMultiname'), null, [ - constant(this.abc.domain), + constant(this.abc.applicationDomain), constant(parameter.type), local ], true); @@ -21425,7 +20184,7 @@ var createName = function createName(namespaces, name) { var methods = this.abc.methods; var classes = this.abc.classes; var multinames = this.abc.constantPool.multinames; - var domain = new Constant(this.abc.domain); + var domain = new Constant(this.abc.applicationDomain); var traceBuilder = c4TraceLevel.value > 2; var stopPoints = []; for (var i = 0; i < blocks.length; i++) { @@ -21566,7 +20325,7 @@ var createName = function createName(namespaces, name) { if (ti) { if (ti.object) { if (ti.object instanceof Global && !ti.object.isExecuting()) { - warn('Can\'t optimize findProperty ' + multiname + ', global object is not yet executed or executing.'); + info('Can\'t optimize findProperty ' + multiname + ', global object is not yet executed or executing.'); return slowPath; } return constant(ti.object); @@ -21574,7 +20333,7 @@ var createName = function createName(namespaces, name) { return getScopeObject(topScope(ti.scopeDepth)); } } - warn('Can\'t optimize findProperty ' + multiname); + info('Can\'t optimize findProperty ' + multiname); return slowPath; } function getJSProperty(object, path) { @@ -21630,6 +20389,32 @@ var createName = function createName(namespaces, name) { } return GlobalMultinameResolver.resolveMultiname(new Multiname(namespaces.value, name.value, multiname.flags)); } + function callSuper(scope, object, multiname, args, ti) { + if (ti && ti.trait && ti.trait.isMethod() && ti.baseClass) { + var qn = VM_OPEN_METHOD_PREFIX + Multiname.getQualifiedName(ti.trait.name); + var callee = getJSProperty(constant(ti.baseClass), 'traitsPrototype.' + qn); + return call(callee, object, args); + } + return store(new IR.ASCallSuper(region, state.store, object, multiname, args, IR.Flags.PRISTINE, scope)); + } + function getSuper(scope, object, multiname, ti) { + if (ti && ti.trait && ti.trait.isGetter() && ti.baseClass) { + var qn = VM_OPEN_GET_METHOD_PREFIX + Multiname.getQualifiedName(ti.trait.name); + var callee = getJSProperty(constant(ti.baseClass), 'traitsPrototype.' + qn); + return call(callee, object, []); + } + return store(new IR.ASGetSuper(region, state.store, object, multiname, scope)); + } + function setSuper(scope, object, multiname, value, ti) { + if (ti && ti.trait && ti.trait.isSetter() && ti.baseClass) { + var qn = VM_OPEN_SET_METHOD_PREFIX + Multiname.getQualifiedName(ti.trait.name); + var callee = getJSProperty(constant(ti.baseClass), 'traitsPrototype.' + qn); + return call(callee, object, [ + value + ]); + } + return store(new IR.ASSetSuper(region, state.store, object, multiname, value, scope)); + } function callProperty(object, multiname, args, isLex, ti) { if (ti && ti.trait) { if (ti.trait.isMethod()) { @@ -21677,7 +20462,7 @@ var createName = function createName(namespaces, name) { return store(new IR.ASGetProperty(region, state.store, object, multiname, IR.Flags.INDEXED | (getOpenMethod ? IR.Flagas.IS_METHOD : 0))); } } - warn('Can\'t optimize getProperty ' + multiname); + info('Can\'t optimize getProperty ' + multiname); var qn = resolveMultinameGlobally(multiname); if (qn) { return store(new IR.ASGetProperty(region, state.store, object, constant(Multiname.getQualifiedName(qn)), IR.Flags.RESOLVED | (getOpenMethod ? IR.Flagas.IS_METHOD : 0))); @@ -21689,6 +20474,10 @@ var createName = function createName(namespaces, name) { true; if (ti) { if (ti.trait) { + var coercer = ti.trait.typeName ? getCoercerForType(ti.trait.typeName) : null; + if (coercer) { + value = coercer(value); + } store(new IR.SetProperty(region, state.store, object, qualifiedNameConstant(ti.trait.name), value)); return; } @@ -21700,7 +20489,7 @@ var createName = function createName(namespaces, name) { return store(new IR.ASSetProperty(region, state.store, object, multiname, value, IR.Flags.INDEXED)); } } - warn('Can\'t optimize setProperty ' + multiname); + info('Can\'t optimize setProperty ' + multiname); var qn = resolveMultinameGlobally(multiname); if (qn) { } @@ -21721,6 +20510,7 @@ var createName = function createName(namespaces, name) { return store(new IR.GetProperty(region, state.store, object, constant(slotQn))); } } + info('Can\'t optimize getSlot ' + index); return store(new IR.ASGetSlot(null, state.store, object, index)); } function setSlot(object, index, value, ti) { @@ -21732,6 +20522,7 @@ var createName = function createName(namespaces, name) { return; } } + info('Can\'t optimize setSlot ' + index); store(new IR.ASSetSlot(region, state.store, object, index, value)); } function call(callee, object, args) { @@ -21895,7 +20686,7 @@ var createName = function createName(namespaces, name) { scope.pop(); break; case 100: - push(new IR.ASGlobal(null, topScope())); + push(new IR.ASGlobal(null, savedScope())); break; case 101: push(getScopeObject(state.scope[bc.index])); @@ -21944,22 +20735,13 @@ var createName = function createName(namespaces, name) { case 4: multiname = buildMultiname(bc.index); object = pop(); - push(call(globalProperty('getSuper'), null, [ - savedScope(), - object, - multiname - ])); + push(getSuper(savedScope(), object, multiname, bc.ti)); break; case 5: value = pop(); multiname = buildMultiname(bc.index); object = pop(); - store(call(globalProperty('setSuper'), null, [ - savedScope(), - object, - multiname, - value - ])); + push(setSuper(savedScope(), object, multiname, value, bc.ti)); break; case 241: case 240: @@ -21993,12 +20775,7 @@ var createName = function createName(namespaces, name) { multiname = buildMultiname(bc.index); args = popMany(bc.argCount); object = pop(); - callee = call(globalProperty('getSuper'), null, [ - savedScope(), - object, - multiname - ]); - value = call(callee, object, args); + value = callSuper(savedScope(), object, multiname, args, bc.ti); if (op !== OP_callsupervoid) { push(value); } @@ -23795,27 +22572,19 @@ var Compiler = new (function () { var VariableDeclarator = T.VariableDeclarator; var MemberExpression = T.MemberExpression; var BinaryExpression = T.BinaryExpression; - var SequenceExpression = T.SequenceExpression; var CallExpression = T.CallExpression; var AssignmentExpression = T.AssignmentExpression; var ExpressionStatement = T.ExpressionStatement; var ReturnStatement = T.ReturnStatement; - var Program = T.Program; - var Statement = T.Statement; var FunctionDeclaration = T.FunctionDeclaration; - var FunctionExpression = T.FunctionExpression; var ConditionalExpression = T.ConditionalExpression; var ObjectExpression = T.ObjectExpression; var ArrayExpression = T.ArrayExpression; var UnaryExpression = T.UnaryExpression; var NewExpression = T.NewExpression; var Property = T.Property; - var UpdateExpression = T.UpdateExpression; - var ForStatement = T.ForStatement; var BlockStatement = T.BlockStatement; var ThisExpression = T.ThisExpression; - var TypeAliasDirective = T.TypeAliasDirective; - var CastExpression = T.CastExpression; var ThrowStatement = T.ThrowStatement; var IfStatement = T.IfStatement; var WhileStatement = T.WhileStatement; @@ -23823,12 +22592,8 @@ var Compiler = new (function () { var ContinueStatement = T.ContinueStatement; var SwitchStatement = T.SwitchStatement; var SwitchCase = T.SwitchCase; - var TryStatement = T.TryStatement; - var CatchClause = T.CatchClause; var Block = IR.Block; var Operator = IR.Operator; - var If = IR.If; - var Jump = IR.Jump; var Projection = IR.Projection; var Start = IR.Start; var Control = Looper.Control; @@ -24183,6 +22948,14 @@ var Compiler = new (function () { var isMethod = new Literal(this.flags & IR.Flags.IS_METHOD); return call(property(object, 'asGetProperty'), name.concat(isMethod)); }; + IR.ASGetSuper.prototype.compile = function (cx) { + var scope = compileValue(this.scope, cx); + var object = compileValue(this.object, cx); + var name = compileMultiname(this.name, cx); + return call(property(object, 'asGetSuper'), [ + scope + ].concat(name)); + }; IR.Latch.prototype.compile = function (cx) { return new ConditionalExpression(compileValue(this.condition, cx), compileValue(this.left, cx), compileValue(this.right, cx)); }; @@ -24234,6 +23007,17 @@ var Compiler = new (function () { new ArrayExpression(args) ])); }; + IR.ASCallSuper.prototype.compile = function (cx) { + var scope = compileValue(this.scope, cx); + var object = compileValue(this.object, cx); + var args = this.args.map(function (arg) { + return compileValue(arg, cx); + }); + var name = compileMultiname(this.name, cx); + return call(property(object, 'asCallSuper'), [ + scope + ].concat(name).concat(new ArrayExpression(args))); + }; IR.Call.prototype.compile = function (cx) { var args = this.args.map(function (arg) { return compileValue(arg, cx); @@ -24285,6 +23069,17 @@ var Compiler = new (function () { var name = compileMultiname(this.name, cx); return call(property(object, 'asSetProperty'), name.concat(value)); }; + IR.ASSetSuper.prototype.compile = function (cx) { + var scope = compileValue(this.scope, cx); + var object = compileValue(this.object, cx); + var name = compileMultiname(this.name, cx); + var value = compileValue(this.value, cx); + return call(property(object, 'asSetSuper'), [ + scope + ].concat(name).concat([ + value + ])); + }; IR.ASDeleteProperty.prototype.compile = function (cx) { var object = compileValue(this.object, cx); var name = compileMultiname(this.name, cx); @@ -24429,7 +23224,7 @@ var Compiler = new (function () { } Backend.generate = generate; }(typeof exports === 'undefined' ? Backend = {} : exports)); -var domainOptions = systemOptions.register(new OptionSet('Domain Options')); +var domainOptions = systemOptions.register(new OptionSet('ApplicationDomain Options')); var traceClasses = domainOptions.register(new Option('tc', 'traceClasses', 'boolean', false, 'trace class creation')); var traceDomain = domainOptions.register(new Option('td', 'traceDomain', 'boolean', false, 'trace domain property access')); var EXECUTION_MODE = { @@ -24438,15 +23233,9 @@ var EXECUTION_MODE = { }; function executeScript(script) { var abc = script.abc; - if (disassemble.value) { - abc.trace(new IndentingWriter()); - } - if (traceExecution.value) { - print('Executing: ' + abc.name + ' ' + script); - } true; var global = new Global(script); - if (abc.domain.allowNatives) { + if (abc.applicationDomain.allowNatives) { global[Multiname.getPublicQualifiedName('unsafeJSNative')] = getNative; } script.executing = true; @@ -24466,8 +23255,10 @@ var Glue = createEmptyObject(); Glue.PUBLIC_PROPERTIES = 1; Glue.PUBLIC_METHODS = 2; Glue.ALL = Glue.PUBLIC_PROPERTIES | Glue.PUBLIC_METHODS; -var Domain = function () { - function Domain(vm, base, mode, allowNatives) { +var ApplicationDomain = function () { + function applicationDomain(vm, base, mode, allowNatives) { + true; + true; this.vm = vm; this.abcs = []; this.loadedAbcs = {}; @@ -24485,7 +23276,7 @@ var Domain = function () { this.system = this; } } - Domain.passthroughCallable = function passthroughCallable(f) { + applicationDomain.passthroughCallable = function passthroughCallable(f) { return { call: function ($this) { Array.prototype.shift.call(arguments); @@ -24496,7 +23287,7 @@ var Domain = function () { } }; }; - Domain.coerceCallable = function coerceCallable(type) { + applicationDomain.coerceCallable = function coerceCallable(type) { return { call: function ($this, value) { return asCoerce(type, value); @@ -24506,7 +23297,7 @@ var Domain = function () { } }; }; - Domain.constructingCallable = function constructingCallable(instanceConstructor) { + applicationDomain.constructingCallable = function constructingCallable(instanceConstructor) { return { call: function ($this) { return new Function.bind.apply(instanceConstructor, arguments); @@ -24518,7 +23309,7 @@ var Domain = function () { } }; }; - Domain.prototype = { + applicationDomain.prototype = { getType: function getType(multiname) { return this.getProperty(multiname, true, true); }, @@ -24552,7 +23343,7 @@ var Domain = function () { }, findDomainProperty: function findDomainProperty(multiname, strict, execute) { if (traceDomain.value) { - print('Domain.findDomainProperty: ' + multiname); + print('ApplicationDomain.findDomainProperty: ' + multiname); } var resolved = this.findDefiningScript(multiname, execute); if (resolved) { @@ -24638,7 +23429,7 @@ var Domain = function () { return resolved; } } - Counter.count('Domain: findDefiningScript'); + Counter.count('ApplicationDomain: findDefiningScript'); var abcs = this.abcs; for (var i = 0; i < abcs.length; i++) { var abc = abcs[i]; @@ -24675,7 +23466,6 @@ var Domain = function () { return undefined; }, compileAbc: function compileAbc(abc) { - console.time('Compile ABC: ' + abc.name); this.loadAbc(abc); var writer = new IndentingWriter(); writer.enter('var classes = {'); @@ -24683,19 +23473,16 @@ var Domain = function () { compileScript(abc.scripts[i], writer); } writer.leave('}'); - console.timeEnd('Compile ABC: ' + abc.name); }, executeAbc: function executeAbc(abc) { - console.time('Execute ABC: ' + abc.name); this.loadAbc(abc); executeScript(abc.lastScript); - console.timeEnd('Execute ABC: ' + abc.name); }, loadAbc: function loadAbc(abc) { if (traceExecution.value) { print('Loading: ' + abc.name); } - abc.domain = this; + abc.applicationDomain = this; GlobalMultinameResolver.loadAbc(abc); this.abcs.push(abc); if (!this.base) { @@ -24729,7 +23516,25 @@ var Domain = function () { lastOnly || writer.leave(''); } }; - return Domain; + return applicationDomain; + }(); +var SecurityDomain = function () { + function securityDomain() { + this.compartment = createNewCompartment(); + this.compartment.environment = environment; + this.compartment.homePath = homePath; + this.compartment.eval(snarf('compartment.js')); + this.compartment.release = true; + } + securityDomain.prototype.initializeShell = function (sysMode, appMode) { + var compartment = this.compartment; + compartment.avm2 = new compartment.AVM2(sysMode, appMode); + compartment.avm2.systemDomain.executeAbc(compartment.grabAbc(homePath + 'src/avm2/generated/builtin/builtin.abc')); + compartment.avm2.systemDomain.executeAbc(compartment.grabAbc(homePath + 'src/avm2/generated/shell/shell.abc')); + this.systemDomain = compartment.avm2.systemDomain; + this.applicationDomain = compartment.avm2.applicationDomain; + }; + return securityDomain; }(); var traitsWriter = null; var Binding = function () { @@ -24787,6 +23592,10 @@ var Bindings = function () { this.type = type; this.trait = trait; } + function SlotInfoMap() { + this.byID = []; + this.byQN = createEmptyObject(); + } function patch(patchTargets, value) { true; for (var i = 0; i < patchTargets.length; i++) { @@ -24897,17 +23706,27 @@ var Bindings = function () { return fn; }, 0, String(trait.name)); if (trait.isGetter()) { + defineNonEnumerableProperty(object, VM_OPEN_GET_METHOD_PREFIX + qn, trampoline); trampoline.patchTargets = [ { object: object, get: qn + }, + { + object: object, + name: VM_OPEN_GET_METHOD_PREFIX + qn } ]; } else { + defineNonEnumerableProperty(object, VM_OPEN_SET_METHOD_PREFIX + qn, trampoline); trampoline.patchTargets = [ { object: object, set: qn + }, + { + object: object, + name: VM_OPEN_SET_METHOD_PREFIX + qn } ]; } @@ -24918,7 +23737,7 @@ var Bindings = function () { true; true; true; - defineNonEnumerableProperty(object, VM_SLOTS, []); + defineNonEnumerableProperty(object, VM_SLOTS, new SlotInfoMap()); defineNonEnumerableProperty(object, VM_BINDINGS, []); defineNonEnumerableProperty(object, VM_OPEN_METHODS, createEmptyObject()); defineNonEnumerableProperty(object, 'bindings', this); @@ -24945,7 +23764,9 @@ var Bindings = function () { traitsWriter && traitsWriter.greenLn('Applying Trait ' + trait.kindName() + ': ' + trait); defineNonEnumerableProperty(object, qn, defaultValue); object[VM_BINDINGS].pushUnique(qn); - object[VM_SLOTS][trait.slotId] = new SlotInfo(qn, trait.isConst(), trait.typeName ? domain.getProperty(trait.typeName, false, false) : null, trait); + var slotInfo = new SlotInfo(qn, trait.isConst(), trait.typeName ? domain.getProperty(trait.typeName, false, false) : null, trait); + object[VM_SLOTS].byID[trait.slotId] = slotInfo; + object[VM_SLOTS].byQN[qn] = slotInfo; } } else if (trait.isMethod() || trait.isGetter() || trait.isSetter()) { if (trait.isGetter() || trait.isSetter()) { @@ -25121,10 +23942,11 @@ var InstanceBindings = function () { binding.natives = this.natives; } } - var domain = ii.abc.domain; + var domain = ii.abc.applicationDomain; var interfaces = ii.interfaces; for (var i = 0; i < interfaces.length; i++) { var interface = domain.getProperty(interfaces[i], true, true); + true; copyProperties(this.implementedInterfaces, interface.interfaceBindings.implementedInterfaces); this.implementedInterfaces[Multiname.getQualifiedName(interface.name)] = interface; } @@ -25214,9 +24036,9 @@ var Class = function () { this.instanceConstructor.class = this; } if (!callable) { - callable = Domain.coerceCallable(this); - } else if (callable === Domain.coerceCallable) { - callable = Domain.coerceCallable(this); + callable = ApplicationDomain.coerceCallable(this); + } else if (callable === ApplicationDomain.coerceCallable) { + callable = ApplicationDomain.coerceCallable(this); } defineNonEnumerableProperty(this, 'call', callable.call); defineNonEnumerableProperty(this, 'apply', callable.apply); @@ -25224,12 +24046,12 @@ var Class = function () { Class.createClass = function createClass(classInfo, baseClass, scope) { var ci = classInfo; var ii = ci.instanceInfo; - var domain = ci.abc.domain; + var domain = ci.abc.applicationDomain; var className = Multiname.getName(ii.name); var isNativeClass = ci.native; if (isNativeClass) { - var classBuilder = getNative(ci.native.cls); - if (!classBuilder) { + var buildClass = getNative(ci.native.cls); + if (!buildClass) { unexpected('No native for ' + ci.native.cls); } if (!baseClass) { @@ -25240,7 +24062,7 @@ var Class = function () { var instanceConstructor = createFunction(ii.init, classScope); var cls; if (isNativeClass) { - cls = classBuilder(domain, classScope, instanceConstructor, baseClass); + cls = buildClass(domain, classScope, instanceConstructor, baseClass); } else { cls = new Class(className, instanceConstructor); } @@ -25293,7 +24115,7 @@ var Class = function () { while (s = initializes.pop()) { s.call(obj); } - Counter.count('Initialize Instance'); + Counter.count('Initialize Instance ' + obj.class); }, createInstance: function createInstance(args) { var o = Object.create(this.instanceConstructor.prototype); @@ -25483,7 +24305,7 @@ var Class = function () { writer.enter('traitsPrototype: '); if (traitsPrototype) { writer.enter('VM_SLOTS: '); - writer.writeArray(traitsPrototype[VM_SLOTS].map(function (slot) { + writer.writeArray(traitsPrototype[VM_SLOTS].byID.map(function (slot) { return slot.trait; })); writer.outdent(); @@ -25525,7 +24347,7 @@ var Class = function () { return '[class ' + this.classInfo.instanceInfo.name.name + ']'; } }; - var callable = Domain.coerceCallable(Class); + var callable = ApplicationDomain.coerceCallable(Class); defineNonEnumerableProperty(Class, 'call', callable.call); defineNonEnumerableProperty(Class, 'apply', callable.apply); Class.instanceConstructor = Class; @@ -26178,16 +25000,9 @@ var VM_BINDINGS = 'vm bindings'; var VM_NATIVE_PROTOTYPE_FLAG = 'vm native prototype'; var VM_OPEN_METHODS = 'vm open methods'; var VM_IS_CLASS = 'vm is class'; -var VM_OPEN_METHOD_PREFIX = 'open_'; -var VM_NATIVE_BUILTINS = [ - Object, - Number, - Boolean, - String, - Array, - Date, - RegExp - ]; +var VM_OPEN_METHOD_PREFIX = 'method_'; +var VM_OPEN_SET_METHOD_PREFIX = 'set_'; +var VM_OPEN_GET_METHOD_PREFIX = 'get_'; var VM_NATIVE_BUILTIN_SURROGATES = [ { object: Object, @@ -26308,7 +25123,7 @@ var LazyInitializer = function () { } else if (this.target instanceof ClassInfo) { this.name = Multiname.getQualifiedName(target.instanceInfo.name); initialize = function () { - return target.abc.domain.getProperty(target.instanceInfo.name); + return target.abc.applicationDomain.getProperty(target.instanceInfo.name); }; } else { notImplemented(target); @@ -26420,6 +25235,16 @@ function asSetProperty(namespaces, name, flags, value) { if (this.asSetNumericProperty && Multiname.isNumeric(resolved)) { return this.asSetNumericProperty(resolved, value); } + var slotInfo = this[VM_SLOTS].byQN[resolved]; + if (slotInfo) { + if (slotInfo.const) { + return; + } + var type = slotInfo.type; + if (type && type.coerce) { + value = type.coerce(value); + } + } this[resolved] = value; } function asSetPropertyLikelyNumeric(namespaces, name, flags, value) { @@ -26470,6 +25295,40 @@ function asCallProperty(namespaces, name, flags, isLex, args) { traceCallExecution.value > 0 && callWriter.leave('return ' + toSafeString(result)); return result; } +function asCallSuper(scope, namespaces, name, flags, args) { + if (traceCallExecution.value) { + var receiver = this.class ? this.class.className + ' ' : ''; + callWriter.enter('call super ' + receiver + name + '(' + toSafeArrayString(args) + ') #' + callCounter.count(name)); + } + var baseClass = scope.object.baseClass; + var resolved = baseClass.traitsPrototype.resolveMultinameProperty(namespaces, name, flags); + var openMethods = baseClass.traitsPrototype[VM_OPEN_METHODS]; + var method = openMethods[resolved]; + var result = method.apply(this, args); + traceCallExecution.value > 0 && callWriter.leave('return ' + toSafeString(result)); + return result; +} +function asSetSuper(scope, namespaces, name, flags, value) { + if (traceCallExecution.value) { + var receiver = this.class ? this.class.className + ' ' : ''; + callWriter.enter('set super ' + receiver + name + '(' + toSafeArrayString(args) + ') #' + callCounter.count(name)); + } + var baseClass = scope.object.baseClass; + var resolved = baseClass.traitsPrototype.resolveMultinameProperty(namespaces, name, flags); + baseClass.traitsPrototype[VM_OPEN_SET_METHOD_PREFIX + resolved].call(this, value); + traceCallExecution.value > 0 && callWriter.leave(''); +} +function asGetSuper(scope, namespaces, name, flags) { + if (traceCallExecution.value) { + var receiver = this.class ? this.class.className + ' ' : ''; + callWriter.enter('get super ' + receiver + name + ' #' + callCounter.count(name)); + } + var baseClass = scope.object.baseClass; + var resolved = baseClass.traitsPrototype.resolveMultinameProperty(namespaces, name, flags); + var result = baseClass.traitsPrototype[VM_OPEN_GET_METHOD_PREFIX + resolved].call(this); + traceCallExecution.value > 0 && callWriter.leave('return ' + toSafeString(result)); + return result; +} function construct(constructor, args) { if (constructor.classInfo) { var qn = constructor.classInfo.instanceInfo.name.qualifiedName; @@ -26579,8 +25438,16 @@ function initializeGlobalObject(global) { } }); }); - VM_NATIVE_BUILTINS.forEach(function (o) { - defineReadOnlyProperty(o.prototype, VM_NATIVE_PROTOTYPE_FLAG, true); + [ + 'Object', + 'Number', + 'Boolean', + 'String', + 'Array', + 'Date', + 'RegExp' + ].forEach(function (name) { + defineReadOnlyProperty(global[name].prototype, VM_NATIVE_PROTOTYPE_FLAG, true); }); defineNonEnumerableProperty(global.Object.prototype, 'getNamespaceResolutionMap', getNamespaceResolutionMap); defineNonEnumerableProperty(global.Object.prototype, 'resolveMultinameProperty', resolveMultinameProperty); @@ -26592,6 +25459,9 @@ function initializeGlobalObject(global) { defineNonEnumerableProperty(global.Object.prototype, 'asDefineProperty', asDefineProperty); defineNonEnumerableProperty(global.Object.prototype, 'asDefinePublicProperty', asDefinePublicProperty); defineNonEnumerableProperty(global.Object.prototype, 'asCallProperty', asCallProperty); + defineNonEnumerableProperty(global.Object.prototype, 'asCallSuper', asCallSuper); + defineNonEnumerableProperty(global.Object.prototype, 'asGetSuper', asGetSuper); + defineNonEnumerableProperty(global.Object.prototype, 'asSetSuper', asSetSuper); defineNonEnumerableProperty(global.Object.prototype, 'asCallPublicProperty', asCallPublicProperty); defineNonEnumerableProperty(global.Object.prototype, 'asCallResolvedStringProperty', asCallResolvedStringProperty); defineNonEnumerableProperty(global.Object.prototype, 'asConstructProperty', asConstructProperty); @@ -26622,13 +25492,13 @@ function initializeGlobalObject(global) { defineNonEnumerableProperty(global[name].prototype, 'asGetProperty', asGetPropertyLikelyNumeric); defineNonEnumerableProperty(global[name].prototype, 'asSetProperty', asSetPropertyLikelyNumeric); }); - Array.prototype.asGetProperty = function (namespaces, name, flags) { + global.Array.prototype.asGetProperty = function (namespaces, name, flags) { if (typeof name === 'number') { return this[name]; } return asGetProperty.call(this, namespaces, name, flags); }; - Array.prototype.asSetProperty = function (namespaces, name, flags, value) { + global.Array.prototype.asSetProperty = function (namespaces, name, flags, value) { if (typeof name === 'number') { this[name] = value; return; @@ -26666,15 +25536,15 @@ function publicizeProperties(object) { } } function asGetSlot(object, index) { - return object[object[VM_SLOTS][index].name]; + return object[object[VM_SLOTS].byID[index].name]; } function asSetSlot(object, index, value) { - var binding = object[VM_SLOTS][index]; - if (binding.const) { + var slotInfo = object[VM_SLOTS].byID[index]; + if (slotInfo.const) { return; } - var name = binding.name; - var type = binding.type; + var name = slotInfo.name; + var type = slotInfo.type; if (type && type.coerce) { object[name] = type.coerce(value); } else { @@ -26771,9 +25641,9 @@ var ScopeStack = function () { }(); var Scope = function () { function scope(parent, object, isWith) { - true; this.parent = parent; - this.object = object; + this.object = boxValue(object); + true; this.global = parent ? parent.global : this; this.isWith = isWith; this.cache = createEmptyObject(); @@ -26926,88 +25796,9 @@ function resolveMultiname(object, mn, traitsOnly) { function sliceArguments(args, offset) { return Array.prototype.slice.call(args, offset); } -function hasProperty(object, name) { - object = boxValue(object); - if (object.hasProperty) { - return object.hasProperty(name); - } - return resolveName(object, name) in object; -} function nonProxyingHasProperty(object, name) { return name in object; } -function getSuper(scope, object, mn) { - true; - true; - true; - var superClass = scope.object.baseClass; - true; - var superTraitsPrototype = superClass.instanceConstructor.prototype; - var resolved = mn.isQName() ? mn : resolveMultiname(superTraitsPrototype, mn); - var value = undefined; - if (resolved) { - if (Multiname.isNumeric(resolved) && superTraitsPrototype.asGetNumericProperty) { - value = superTraitsPrototype.asGetNumericProperty(Multiname.getQualifiedName(resolved), value); - } else { - var qn = Multiname.getQualifiedName(resolved); - var openMethod = superTraitsPrototype[VM_OPEN_METHODS][qn]; - var superName = superClass.classInfo.instanceInfo.name; - if (openMethod) { - value = object[superName + ' ' + qn]; - if (!value) { - value = object[superName + ' ' + qn] = bindSafely(openMethod, object); - } - } else { - var descriptor = Object.getOwnPropertyDescriptor(superTraitsPrototype, qn); - true; - value = descriptor.get ? descriptor.get.call(object) : object[qn]; - } - } - } - return value; -} -function resolveName(object, name) { - if (name instanceof Multiname) { - if (name.namespaces.length > 1) { - var resolved = resolveMultiname(object, name); - if (resolved !== undefined) { - return Multiname.getQualifiedName(resolved); - } else { - return Multiname.getPublicQualifiedName(name.name); - } - } else { - return Multiname.getQualifiedName(name); - } - } else if (typeof name === 'object') { - return Multiname.getPublicQualifiedName(String(name)); - } else { - return name; - } -} -function setSuper(scope, object, mn, value) { - true; - true; - var superClass = scope.object.baseClass; - true; - var superTraitsPrototype = superClass.instanceConstructor.prototype; - var resolved = Multiname.isQName(mn) ? mn : resolveMultiname(superTraitsPrototype, mn); - if (resolved !== undefined) { - if (Multiname.isNumeric(resolved) && superTraitsPrototype.asSetNumericProperty) { - superTraitsPrototype.asSetNumericProperty(Multiname.getQualifiedName(resolved), value); - } else { - var qn = Multiname.getQualifiedName(resolved); - var descriptor = Object.getOwnPropertyDescriptor(superTraitsPrototype, qn); - true; - if (descriptor.set) { - descriptor.set.call(object, value); - } else { - object[qn] = value; - } - } - } else { - throw new ReferenceError('Cannot create property ' + mn.name + ' on ' + superClass.debugName); - } -} function forEachPublicProperty(object, fn, self) { if (!object[VM_BINDINGS]) { for (var key in object) { @@ -27062,29 +25853,29 @@ function CatchScopeObject(domain, trait) { } } var Global = function () { - function Global(script) { + function global(script) { this.scriptInfo = script; script.global = this; script.scriptBindings = new ScriptBindings(script, new Scope(null, this)); - script.scriptBindings.applyTo(script.abc.domain, this); + script.scriptBindings.applyTo(script.abc.applicationDomain, this); script.loaded = true; } - Global.prototype.toString = function () { + global.prototype.toString = function () { return '[object global]'; }; - Global.prototype.isExecuted = function () { + global.prototype.isExecuted = function () { return this.scriptInfo.executed; }; - Global.prototype.isExecuting = function () { + global.prototype.isExecuting = function () { return this.scriptInfo.executing; }; - Global.prototype.ensureExecuted = function () { + global.prototype.ensureExecuted = function () { ensureScriptIsExecuted(this.scriptInfo); }; - defineNonEnumerableProperty(Global.prototype, Multiname.getPublicQualifiedName('toString'), function () { + defineNonEnumerableProperty(global.prototype, Multiname.getPublicQualifiedName('toString'), function () { return this.toString(); }); - return Global; + return global; }(); function canCompile(mi) { if (!mi.hasBody) { @@ -27172,24 +25963,8 @@ function debugName(value) { } return value; } -function createCompiledFunctionTrampoline(methodInfo, scope, hasDynamicScope, breakpoint) { - var mi = methodInfo; - return function trampolineContext() { - var fn; - return function () { - if (!fn) { - fn = mi.freeMethod = createCompiledFunction(mi, scope, hasDynamicScope, breakpoint); - mi.freeMethod.methodInfo = mi; - } - return fn.apply(this, arguments); - }; - }(); -} function createCompiledFunction(methodInfo, scope, hasDynamicScope, breakpoint, deferCompilation) { var mi = methodInfo; - if (false && deferCompilation) { - return createCompiledFunctionTrampoline(methodInfo, scope, hasDynamicScope, breakpoint); - } $M.push(mi); var result = Compiler.compileMethod(mi, scope, hasDynamicScope); var parameters = result.parameters; @@ -27217,11 +25992,9 @@ function createCompiledFunction(methodInfo, scope, hasDynamicScope, breakpoint, if (traceLevel.value > 1) { mi.trace(new IndentingWriter(), mi.abc); } - mi.debugTrace = function (abc) { - return function () { - mi.trace(new IndentingWriter(), abc); - }; - }(this.abc); + mi.debugTrace = function () { + mi.trace(new IndentingWriter(), mi.abc); + }; if (traceLevel.value > 0) { print(fnSource); } @@ -27229,16 +26002,20 @@ function createCompiledFunction(methodInfo, scope, hasDynamicScope, breakpoint, fn.debugName = 'Compiled Function #' + vmNextCompiledFunctionId++; return fn; } +function getMethodOverrideKey(methodInfo) { + var key; + if (methodInfo.holder instanceof ClassInfo) { + key = 'static ' + methodInfo.holder.instanceInfo.name.getOriginalName() + '::' + methodInfo.name.getOriginalName(); + } else if (methodInfo.holder instanceof InstanceInfo) { + key = methodInfo.holder.name.getOriginalName() + '::' + methodInfo.name.getOriginalName(); + } else { + key = methodInfo.name.getOriginalName(); + } + return key; +} function checkMethodOverrides(methodInfo) { if (methodInfo.name) { - var key; - if (methodInfo.holder instanceof ClassInfo) { - key = 'static ' + methodInfo.holder.instanceInfo.name.getOriginalName() + '::' + methodInfo.name.getOriginalName(); - } else if (methodInfo.holder instanceof InstanceInfo) { - key = methodInfo.holder.name.getOriginalName() + '::' + methodInfo.name.getOriginalName(); - } else { - key = methodInfo.name.getOriginalName(); - } + var key = getMethodOverrideKey(methodInfo); if (key in VM_METHOD_OVERRIDES) { warning('Overriding Method: ' + key); return VM_METHOD_OVERRIDES[key]; @@ -27332,7 +26109,7 @@ function createFunction(mi, scope, hasDynamicScope, breakpoint) { ensureFunctionIsInitialized(mi); totalFunctionCount++; var useInterpreter = false; - if ((mi.abc.domain.mode === EXECUTION_MODE.INTERPRET || !shouldCompile(mi)) && !forceCompile(mi)) { + if ((mi.abc.applicationDomain.mode === EXECUTION_MODE.INTERPRET || !shouldCompile(mi)) && !forceCompile(mi)) { useInterpreter = true; } if (compileOnly.value >= 0) { @@ -27373,7 +26150,7 @@ function ensureFunctionIsInitialized(methodInfo) { mi.analysis = new Analysis(mi); if (mi.needsActivation()) { mi.activationPrototype = new Activation(mi); - new ActivationBindings(mi).applyTo(mi.abc.domain, mi.activationPrototype); + new ActivationBindings(mi).applyTo(mi.abc.applicationDomain, mi.activationPrototype); } var exceptions = mi.exceptions; for (var i = 0, j = exceptions.length; i < j; i++) { @@ -27384,7 +26161,7 @@ function ensureFunctionIsInitialized(methodInfo) { varTrait.name = handler.varName; varTrait.typeName = handler.typeName; varTrait.holder = mi; - handler.scopeObject = new CatchScopeObject(mi.abc.domain, varTrait); + handler.scopeObject = new CatchScopeObject(mi.abc.applicationDomain, varTrait); } else { handler.scopeObject = new CatchScopeObject(); } @@ -27445,7 +26222,7 @@ function createClass(classInfo, baseClass, scope) { true; var ci = classInfo; var ii = ci.instanceInfo; - var domain = ci.abc.domain; + var domain = ci.abc.applicationDomain; var className = Multiname.getName(ii.name); if (traceExecution.value) { print('Creating ' + (ii.isInterface() ? 'Interface' : 'Class') + ': ' + className + (ci.native ? ' replaced with native ' + ci.native.cls : '')); @@ -27519,8 +26296,22 @@ function applyType(domain, factory, types) { return notImplemented(factoryClassName); } } +function checkArgumentCount(name, expected, got) { + if (got !== expected) { + throwError('ArgumentError', Errors.WrongArgumentCountError, name, expected, got); + } +} +function throwError(name, error) { + if (true) { + var message = formatErrorMessage.apply(null, Array.prototype.slice.call(arguments, 1)); + throwErrorFromVM(AVM2.currentDomain(), name, message, error.code); + } else { + throwErrorFromVM(AVM2.currentDomain(), name, getErrorMessage(error.code), error.code); + } +} function throwErrorFromVM(domain, errorClass, message, id) { - throw new (domain.getClass(errorClass)).instanceConstructor(message, id); + var error = new (domain.getClass(errorClass)).instanceConstructor(message, id); + throw error; } function translateError(domain, error) { if (error instanceof Error) { @@ -27605,13 +26396,16 @@ function asDefaultCompareFunction(a, b) { return String(a).localeCompare(String(b)); } function asCompare(a, b, options, compareFunction) { - true; true; true; var result = 0; if (!compareFunction) { compareFunction = asDefaultCompareFunction; } + if (options & SORT_CASEINSENSITIVE) { + a = String(a).toLowerCase(); + b = String(b).toLowerCase(); + } if (options & SORT_NUMERIC) { a = toNumber(a); b = toNumber(b); @@ -27691,16 +26485,29 @@ VM_METHOD_OVERRIDES['com.midasplayer.debug.DebugLog::trace'] = function (msg) { VM_METHOD_OVERRIDES['com.midasplayer.engine.comm.DebugGameComm::getGameData'] = function () { return '\ntrue\ntrue\nfalse\n0\n0\n0\n0\n0\n0\n0\n0\n0\n0\nCandy crushed\nbest ever\nscored {0} in one game\nAll Clear Created\ncrushed {0} candy in one game\nScore\nPlease register to play the full game\nLongest chain\nGame ends in {0} seconds\nSuper Stripes Created\nNo more moves!\nMega-Candy Created\nGame starts in {0} seconds\nnow\nLevel reached\nGame Over\nMatch 3 Candy of the same colour to crush them. Matching 4 or 5 in different formations generates special sweets that are extra tasty.\nYou can also combine the special sweets for additional effects by switching them with each other. Try these combinations for a taste you will not forget: \nDouble Colour Bombs Created\nmade {0} combined candy in one game\nPlay like this:\n'; }; +VM_METHOD_OVERRIDES['com.antkarlov.Preloader::com.antkarlov:Preloader.isUrl'] = function () { + return true; +}; +function asCheckVectorSetNumericProperty(i, length, fixed) { + if (i < 0 || i > length || i === length && fixed || !isNumeric(i)) { + throwError('RangeError', Errors.OutOfRangeError, i, length); + } +} +function asCheckVectorGetNumericProperty(i, length) { + if (i < 0 || i >= length || !isNumeric(i)) { + throwError('RangeError', Errors.OutOfRangeError, i, length); + } +} var TypedArrayVector = function () { var EXTRA_CAPACITY = 4; var INITIAL_CAPACITY = 10; + var DEFAULT_VALUE = 0; function vector(length, fixed) { length = length | 0; - this.fixed = !(!fixed); - this.buffer = new Int32Array(Math.max(INITIAL_CAPACITY, length + EXTRA_CAPACITY)); - this.offset = 0; + this._fixed = !(!fixed); + this._buffer = new Int32Array(Math.max(INITIAL_CAPACITY, length + EXTRA_CAPACITY)); + this._offset = 0; this._length = length; - this.defaultValue = 0; } vector.callable = function (object) { if (object instanceof vector) { @@ -27718,29 +26525,29 @@ var TypedArrayVector = function () { }; vector.prototype.internalToString = function () { var str = ''; - var start = this.offset; + var start = this._offset; var end = start + this._length; - for (var i = 0; i < this.buffer.length; i++) { + for (var i = 0; i < this._buffer.length; i++) { if (i === start) { str += '['; } if (i === end) { str += ']'; } - str += this.buffer[i]; - if (i < this.buffer.length - 1) { + str += this._buffer[i]; + if (i < this._buffer.length - 1) { str += ','; } } - if (this.offset + this._length === this.buffer.length) { + if (this._offset + this._length === this._buffer.length) { str += ']'; } - return str + ': offset: ' + this.offset + ', length: ' + this._length + ', capacity: ' + this.buffer.length; + return str + ': offset: ' + this._offset + ', length: ' + this._length + ', capacity: ' + this._buffer.length; }; vector.prototype.toString = function () { var str = ''; for (var i = 0; i < this._length; i++) { - str += this.buffer[this.offset + i]; + str += this._buffer[this._offset + i]; if (i < this._length - 1) { str += ','; } @@ -27748,27 +26555,27 @@ var TypedArrayVector = function () { return str; }; vector.prototype._view = function () { - return this.buffer.subarray(this.offset, this.offset + this._length); + return this._buffer.subarray(this._offset, this._offset + this._length); }; vector.prototype._ensureCapacity = function (length) { - var minCapacity = this.offset + length; - if (minCapacity < this.buffer.length) { + var minCapacity = this._offset + length; + if (minCapacity < this._buffer.length) { return; } - if (length <= this.buffer.length) { - var offset = this.buffer.length - length >> 2; - this.buffer.set(this._view(), offset); - this.offset = offset; + if (length <= this._buffer.length) { + var offset = this._buffer.length - length >> 2; + this._buffer.set(this._view(), offset); + this._offset = offset; return; } - var oldCapacity = this.buffer.length; + var oldCapacity = this._buffer.length; var newCapacity = oldCapacity * 3 >> 2; if (newCapacity < minCapacity) { newCapacity = minCapacity; } var buffer = new Int32Array(newCapacity); - buffer.set(this.buffer, 0); - this.buffer = buffer; + buffer.set(this._buffer, 0); + this._buffer = buffer; }; vector.prototype.concat = function () { notImplemented('TypedArrayVector.concat'); @@ -27790,6 +26597,19 @@ var TypedArrayVector = function () { } return v; }; + vector.prototype.some = function (callback, thisObject) { + if (arguments.length !== 2) { + throwError('ArgumentError', Errors.WrongArgumentCountError); + } else if (!isFunction(callback)) { + throwError('ArgumentError', Errors.CheckTypeFailedError); + } + for (var i = 0; i < this._length; i++) { + if (callback.call(thisObject, this.asGetNumericProperty(i), i, this)) { + return true; + } + } + return false; + }; vector.prototype.forEach = function (callback, thisObject) { for (var i = 0; i < this._length; i++) { callback.call(thisObject, this.asGetNumericProperty(i), i, this); @@ -27805,26 +26625,34 @@ var TypedArrayVector = function () { notImplemented('TypedArrayVector.lastIndexOf'); }; vector.prototype.map = function (callback, thisObject) { - notImplemented('TypedArrayVector.map'); + if (!isFunction(callback)) { + throwError('ArgumentError', Errors.CheckTypeFailedError); + } + var v = new vector(); + for (var i = 0; i < this._length; i++) { + v.push(callback.call(thisObject, this.asGetNumericProperty(i), i, this)); + } + return v; }; vector.prototype.push = function () { + this._checkFixed(); this._ensureCapacity(this._length + arguments.length); for (var i = 0; i < arguments.length; i++) { - this.buffer[this.offset + this._length++] = arguments[i]; + this._buffer[this._offset + this._length++] = arguments[i]; } }; vector.prototype.pop = function () { this._checkFixed(); if (this._length === 0) { - return this.defaultValue; + return DEFAULT_VALUE; } this._length--; - return this.buffer[this.offset + this._length]; + return this._buffer[this._offset + this._length]; }; vector.prototype.reverse = function () { - var l = this.offset; - var r = this.offset + this._length - 1; - var b = this.buffer; + var l = this._offset; + var r = this._offset + this._length - 1; + var b = this._buffer; while (l < r) { var t = b[l]; b[l] = b[r]; @@ -27961,40 +26789,45 @@ var TypedArrayVector = function () { }); }; vector.prototype.asGetNumericProperty = function (i) { - return this.buffer[this.offset + i]; + checkArguments && asCheckVectorGetNumericProperty(i, this._length); + return this._buffer[this._offset + i]; }; vector.prototype.asSetNumericProperty = function (i, v) { + checkArguments && asCheckVectorSetNumericProperty(i, this._length, this._fixed); if (i === this._length) { this._ensureCapacity(this._length + 1); this._length++; } - this.buffer[this.offset + i] = v; + this._buffer[this._offset + i] = v; }; vector.prototype.shift = function () { this._checkFixed(); if (this._length === 0) { - return undefined; + return 0; } this._length--; - return this.buffer[this.offset++]; + return this._buffer[this._offset++]; }; vector.prototype._checkFixed = function () { - if (this.fixed) { - var error = Errors.VectorFixedError; - throwErrorFromVM(AVM2.currentDomain(), 'RangeError', getErrorMessage(error.code), error.code); + if (this._fixed) { + throwError('RangeError', Errors.VectorFixedError); } }; vector.prototype._slide = function (distance) { - this.buffer.set(this._view(), this.offset + distance); - this.offset += distance; + this._buffer.set(this._view(), this._offset + distance); + this._offset += distance; }; vector.prototype.unshift = function () { this._checkFixed(); + if (!arguments.length) { + return; + } + this._ensureCapacity(this._length + arguments.length); this._slide(arguments.length); - this.offset -= arguments.length; - this.length += arguments.length; + this._offset -= arguments.length; + this._length += arguments.length; for (var i = 0; i < arguments.length; i++) { - this.buffer[this.offset + i] = arguments[i]; + this._buffer[this._offset + i] = arguments[i]; } }; vector.prototype.asGetEnumerableKeys = function () { @@ -28002,7 +26835,7 @@ var TypedArrayVector = function () { return Object.prototype.asGetEnumerableKeys.call(this); } var keys = []; - for (var i = 0; i < this.length; i++) { + for (var i = 0; i < this._length; i++) { keys.push(i); } return keys; @@ -28022,8 +26855,8 @@ var TypedArrayVector = function () { length = length >>> 0; if (length > this._length) { this._ensureCapacity(length); - for (var i = this.offset + this._length, j = this.offset + length; i < j; i++) { - this.buffer[i] = this.defaultValue; + for (var i = this._offset + this._length, j = this._offset + length; i < j; i++) { + this._buffer[i] = DEFAULT_VALUE; } } this._length = length; @@ -28033,12 +26866,12 @@ var TypedArrayVector = function () { insertCount = clamp(insertCount, 0, args.length - offset); deleteCount = clamp(deleteCount, 0, this._length - index); this._ensureCapacity(this._length - deleteCount + insertCount); - var right = this.offset + index + deleteCount; - var slice = this.buffer.subarray(right, right + this._length - index - deleteCount); - this.buffer.set(slice, this.offset + index + insertCount); + var right = this._offset + index + deleteCount; + var slice = this._buffer.subarray(right, right + this._length - index - deleteCount); + this._buffer.set(slice, this._offset + index + insertCount); this._length += insertCount - deleteCount; for (var i = 0; i < insertCount; i++) { - this.buffer[this.offset + index + i] = args.asGetNumericProperty(offset + i); + this._buffer[this._offset + index + i] = args.asGetNumericProperty(offset + i); } }; vector.prototype.asGetEnumerableKeys = function () { @@ -28053,224 +26886,11 @@ var TypedArrayVector = function () { }; return vector; }(); -var typedArrayVectorTemplate = 'var EXTRA_CAPACITY=4,INITIAL_CAPACITY=10;function vector(a,b){a|=0;this.fixed=!!b;this.buffer=new Int32Array(Math.max(INITIAL_CAPACITY,a+EXTRA_CAPACITY));this.offset=0;this._length=a;this.defaultValue=0}vector.callable=function(a){if(a instanceof vector)return a;var b=a.asGetProperty(void 0,"length");if(void 0!==b){for(var c=new vector(b,!1),d=0;d>2,this.buffer.set(this._view(),b),this.offset=b):(a=3*this.buffer.length>>2,ab?1:0):f=d(a,b);c&vector.DESCENDING&&(f*=-1);return f} function _sort(a){for(var b=[],c=-1,d=0,f=a.length-1,e,g,h,k;;)if(100>=f-d){for(g=d+1;g<=f;g++){h=a[g];for(e=g-1;e>=d&&a[e]>h;)a[e+1]=a[e--];a[e+1]=h}if(-1==c)break;f=b[c--];d=b[c--]}else{k=d+f>>1;e=d+1;g=f;h=a[k];a[k]=a[e];a[e]=h;a[d]>a[f]&&(h=a[d],a[d]=a[f],a[f]=h);a[e]>a[f]&&(h=a[e],a[e]=a[f],a[f]=h);a[d]>a[e]&&(h=a[d],a[d]=a[e],a[e]=h);for(k=a[e];;){do e++;while(a[e]k);if(g=g-d?(b[++c]=e,b[++c]=f,f=g-1):(b[++c]=d, b[++c]=g-1,d=e)}return a}vector.prototype._sortNumeric=function(a){_sort(this._view());a&&this.reverse()};vector.prototype.sort=function(){if(0===arguments.length)return Array.prototype.sort.call(this._view());var a,b=0;arguments[0]instanceof Function?a=arguments[0]:isNumber(arguments[0])&&(b=arguments[0]);isNumber(arguments[1])&&(b=arguments[1]);if(b&TypedArrayVector.NUMERIC)return this._sortNumeric(b&vector.DESCENDING);Array.prototype.sort.call(this._view(),function(c,d){return compare(c,d,b,a)})}; vector.prototype.asGetNumericProperty=function(a){return this.buffer[this.offset+a]};vector.prototype.asSetNumericProperty=function(a,b){a===this._length&&(this._ensureCapacity(this._length+1),this._length++);this.buffer[this.offset+a]=b};vector.prototype.shift=function(){this._checkFixed();if(0!==this._length)return this._length--,this.buffer[this.offset++]}; vector.prototype._checkFixed=function(){if(this.fixed){var a=Errors.VectorFixedError;throwErrorFromVM(AVM2.currentDomain(),"RangeError",getErrorMessage(a.code),a.code)}};vector.prototype._slide=function(a){this.buffer.set(this._view(),this.offset+a);this.offset+=a};vector.prototype.unshift=function(){this._checkFixed();this._slide(arguments.length);this.offset-=arguments.length;this.length+=arguments.length;for(var a=0;a>>=0;if(a>this._length){this._ensureCapacity(a);for(var b=this.offset+this._length,c=this.offset+a;b b ? 1 : 0; - } else { - result = compareFunction(a, b); - } - if (options & DESCENDING) { - result *= -1; - } - return result; - } - vector.prototype.sort = function () { - if (arguments.length === 0) { - return this.buffer.sort(); - } - var compareFunction, options = 0; - if (arguments[0] instanceof Function) { - compareFunction = arguments[0]; - } else if (isNumber(arguments[0])) { - options = arguments[0]; - } - if (isNumber(arguments[1])) { - options = arguments[1]; - } - if (options & TypedArrayVector.NUMERIC) { - return this._sortNumeric(options & vector.DESCENDING); - } - Array.prototype.sort.call(this.buffer, function (a, b) { - return compare(a, b, options, compareFunction); - }); - }; - vector.prototype.asGetNumericProperty = function (i) { - return this.buffer[i]; - }; - vector.prototype._coerce = function (v) { - if (this.type) { - return this.type.coerce(v); - } else if (v === undefined) { - return null; - } - return v; - }; - vector.prototype.asSetNumericProperty = function (i, v) { - this.buffer[i] = this._coerce(v); - }; - vector.prototype.shift = function () { - this._checkFixed(); - if (this.buffer.length === 0) { - return undefined; - } - return this.buffer.shift(); - }; - vector.prototype._checkFixed = function () { - if (this.fixed) { - var error = Errors.VectorFixedError; - throwErrorFromVM(AVM2.currentDomain(), 'RangeError', getErrorMessage(error.code), error.code); - } - }; - vector.prototype.unshift = function () { - this._checkFixed(); - var items = []; - for (var i = 0; i < arguments.length; i++) { - items.push(this._coerce(arguments[i])); - } - this.buffer.unshift.apply(this.buffer, items); - }; - Object.defineProperty(vector.prototype, 'length', { - get: function () { - return this.buffer.length; - }, - set: function (length) { - length = length >>> 0; - if (length > this.buffer.length) { - for (var i = this.buffer.length; i < length; i++) { - this.buffer[i] = this.defaultValue; - } - } else { - this.buffer.length = length; - } - true; - } - }); - vector.prototype._spliceHelper = function (index, insertCount, deleteCount, args, offset) { - insertCount = clamp(insertCount, 0, args.length - offset); - deleteCount = clamp(deleteCount, 0, this.buffer.length - index); - var items = []; - for (var i = 0; i < insertCount; i++) { - items.push(this._coerce(args.asGetNumericProperty(offset + i))); - } - this.buffer.splice.apply(this.buffer, [ - index, - deleteCount - ].concat(items)); - }; - vector.prototype.asGetEnumerableKeys = function () { - if (vector.prototype === this) { - return Object.prototype.asGetEnumerableKeys.call(this); - } - var keys = []; - for (var i = 0; i < this.buffer.length; i++) { - keys.push(i); - } - return keys; - }; - vector.prototype.asHasProperty = function (namespaces, name, flags) { - if (vector.prototype === this || !isNumeric(name)) { - return Object.prototype.asHasProperty.call(this, namespaces, name, flags); - } - var index = toNumber(name); - return index >= 0 && index < this.buffer.length; - }; - return vector; - }(); +Int32Vector = TypedArrayVector; Int32Vector.prototype.asGetProperty = function (namespaces, name, flags) { if (typeof name === 'number') { return this.asGetNumericProperty(name); @@ -28310,6 +26930,233 @@ Float64Vector.prototype.asSetProperty = function (namespaces, name, flags, value } return asSetProperty.call(this, namespaces, name, flags, value); }; +var GenericVector = function () { + function vector(length, fixed, type) { + length = length | 0; + this._fixed = !(!fixed); + this._buffer = new Array(length); + this._type = type; + this._defaultValue = type ? type.defaultValue : null; + this._fill(0, length, this._defaultValue); + } + vector.applyType = function applyType(type) { + function parameterizedVector(length, fixed) { + vector.call(this, length, fixed, type); + } + parameterizedVector.prototype = Object.create(vector.prototype); + parameterizedVector.callable = vector.callable; + return parameterizedVector; + }; + vector.callable = function (object) { + if (object instanceof vector) { + return object; + } + var length = object.asGetProperty(undefined, 'length'); + if (length !== undefined) { + var v = new vector(length, false); + for (var i = 0; i < length; i++) { + v.asSetNumericProperty(i, object.asGetPublicProperty(i)); + } + return v; + } + unexpected(); + }; + vector.prototype._fill = function (index, length, value) { + for (var i = 0; i < length; i++) { + this._buffer[index + i] = value; + } + }; + vector.prototype.toString = function () { + var str = ''; + for (var i = 0; i < this._buffer.length; i++) { + str += this._buffer[i]; + if (i < this._buffer.length - 1) { + str += ','; + } + } + return str; + }; + vector.prototype.every = function (callback, thisObject) { + for (var i = 0; i < this._buffer.length; i++) { + if (!callback.call(thisObject, this.asGetNumericProperty(i), i, this)) { + return false; + } + } + return true; + }; + vector.prototype.filter = function (callback, thisObject) { + var v = new vector(); + for (var i = 0; i < this._buffer.length; i++) { + if (callback.call(thisObject, this.asGetNumericProperty(i), i, this)) { + v.push(this.asGetNumericProperty(i)); + } + } + return v; + }; + vector.prototype.some = function (callback, thisObject) { + if (arguments.length !== 2) { + throwError('ArgumentError', Errors.WrongArgumentCountError); + } else if (!isFunction(callback)) { + throwError('ArgumentError', Errors.CheckTypeFailedError); + } + for (var i = 0; i < this._buffer.length; i++) { + if (callback.call(thisObject, this.asGetNumericProperty(i), i, this)) { + return true; + } + } + return false; + }; + vector.prototype.forEach = function (callback, thisObject) { + if (!isFunction(callback)) { + throwError('ArgumentError', Errors.CheckTypeFailedError); + } + for (var i = 0; i < this._buffer.length; i++) { + callback.call(thisObject, this.asGetNumericProperty(i), i, this); + } + }; + vector.prototype.map = function (callback, thisObject) { + if (!isFunction(callback)) { + throwError('ArgumentError', Errors.CheckTypeFailedError); + } + var v = new vector(); + for (var i = 0; i < this._buffer.length; i++) { + v.push(callback.call(thisObject, this.asGetNumericProperty(i), i, this)); + } + return v; + }; + vector.prototype.push = function () { + this._checkFixed(); + for (var i = 0; i < arguments.length; i++) { + this._buffer.push(this._coerce(arguments[i])); + } + }; + vector.prototype.pop = function () { + this._checkFixed(); + if (this._buffer.length === 0) { + return undefined; + } + return this._buffer.pop(); + }; + vector.prototype.reverse = function () { + this._buffer.reverse(); + }; + vector.CASEINSENSITIVE = 1; + vector.DESCENDING = 2; + vector.UNIQUESORT = 4; + vector.RETURNINDEXEDARRAY = 8; + vector.NUMERIC = 16; + function defaultCompareFunction(a, b) { + return String(a).localeCompare(String(b)); + } + function compare(a, b, options, compareFunction) { + assertNotImplemented(!(options & CASEINSENSITIVE), 'CASEINSENSITIVE'); + assertNotImplemented(!(options & UNIQUESORT), 'UNIQUESORT'); + assertNotImplemented(!(options & RETURNINDEXEDARRAY), 'RETURNINDEXEDARRAY'); + var result = 0; + if (!compareFunction) { + compareFunction = defaultCompareFunction; + } + if (options & NUMERIC) { + a = toNumber(a); + b = toNumber(b); + result = a < b ? -1 : a > b ? 1 : 0; + } else { + result = compareFunction(a, b); + } + if (options & DESCENDING) { + result *= -1; + } + return result; + } + vector.prototype.sort = function (comparator) { + return this._buffer.sort(comparator); + }; + vector.prototype.asGetNumericProperty = function (i) { + checkArguments && asCheckVectorGetNumericProperty(i, this._buffer.length); + return this._buffer[i]; + }; + vector.prototype._coerce = function (v) { + if (this._type) { + return this._type.coerce(v); + } else if (v === undefined) { + return null; + } + return v; + }; + vector.prototype.asSetNumericProperty = function (i, v) { + checkArguments && asCheckVectorSetNumericProperty(i, this._buffer.length, this._fixed); + this._buffer[i] = this._coerce(v); + }; + vector.prototype.shift = function () { + this._checkFixed(); + if (this._buffer.length === 0) { + return undefined; + } + return this._buffer.shift(); + }; + vector.prototype._checkFixed = function () { + if (this._fixed) { + throwError('RangeError', Errors.VectorFixedError); + } + }; + vector.prototype.unshift = function () { + if (!arguments.length) { + return; + } + this._checkFixed(); + var items = []; + for (var i = 0; i < arguments.length; i++) { + items.push(this._coerce(arguments[i])); + } + this._buffer.unshift.apply(this._buffer, items); + }; + Object.defineProperty(vector.prototype, 'length', { + get: function () { + return this._buffer.length; + }, + set: function (length) { + length = length >>> 0; + if (length > this._buffer.length) { + for (var i = this._buffer.length; i < length; i++) { + this._buffer[i] = this._defaultValue; + } + } else { + this._buffer.length = length; + } + true; + } + }); + vector.prototype._spliceHelper = function (index, insertCount, deleteCount, args, offset) { + insertCount = clamp(insertCount, 0, args.length - offset); + deleteCount = clamp(deleteCount, 0, this._buffer.length - index); + var items = []; + for (var i = 0; i < insertCount; i++) { + items.push(this._coerce(args.asGetNumericProperty(offset + i))); + } + this._buffer.splice.apply(this._buffer, [ + index, + deleteCount + ].concat(items)); + }; + vector.prototype.asGetEnumerableKeys = function () { + if (vector.prototype === this) { + return Object.prototype.asGetEnumerableKeys.call(this); + } + var keys = []; + for (var i = 0; i < this._buffer.length; i++) { + keys.push(i); + } + return keys; + }; + vector.prototype.asHasProperty = function (namespaces, name, flags) { + if (vector.prototype === this || !isNumeric(name)) { + return Object.prototype.asHasProperty.call(this, namespaces, name, flags); + } + var index = toNumber(name); + return index >= 0 && index < this._buffer.length; + }; + return vector; + }(); GenericVector.prototype.asGetProperty = function (namespaces, name, flags) { if (typeof name === 'number') { return this.asGetNumericProperty(name); @@ -28323,6 +27170,158 @@ GenericVector.prototype.asSetProperty = function (namespaces, name, flags, value } return asSetProperty.call(this, namespaces, name, flags, value); }; +function arraySort(o, args) { + if (args.length === 0) { + return o.sort(); + } + var compareFunction, options = 0; + if (args[0] instanceof Function) { + compareFunction = args[0]; + } else if (isNumber(args[0])) { + options = args[0]; + } + if (isNumber(args[1])) { + options = args[1]; + } + o.sort(function (a, b) { + return asCompare(a, b, options, compareFunction); + }); + return o; +} +function ArrayClass(domain, scope, instanceConstructor, baseClass) { + var c = new Class('Array', Array, ApplicationDomain.passthroughCallable(Array)); + c.extendBuiltin(baseClass); + var CACHE_NUMERIC_COMPARATORS = true; + var numericComparatorCache = createEmptyObject(); + c.native = { + static: { + _pop: function _pop(o) { + return o.pop(); + }, + _reverse: function _reverse(o) { + return o.reverse(); + }, + _concat: function _concat(o, args) { + return o.concat.apply(o, args); + }, + _shift: function _shift(o) { + return o.shift(); + }, + _slice: function _slice(o, A, B) { + return o.slice(A, B); + }, + _unshift: function _unshift(o, args) { + return o.unshift.apply(o, args); + }, + _splice: function _splice(o, args) { + return o.splice.apply(o, args); + }, + _sort: function _sort(o, args) { + if (args.length === 0) { + return o.sort(); + } + var compareFunction, options = 0; + if (args[0] instanceof Function) { + compareFunction = args[0]; + } else if (isNumber(args[0])) { + options = args[0]; + } + if (isNumber(args[1])) { + options = args[1]; + } + o.sort(function (a, b) { + return asCompare(a, b, options, compareFunction); + }); + return o; + }, + _sortOn: function _sortOn(o, names, options) { + if (isString(names)) { + names = [ + names + ]; + } + if (isNumber(options)) { + options = [ + options + ]; + } + for (var i = names.length - 1; i >= 0; i--) { + var key = Multiname.getPublicQualifiedName(names[i]); + if (CACHE_NUMERIC_COMPARATORS && options[i] & SORT_NUMERIC) { + var str = 'var x = toNumber(a.' + key + '), y = toNumber(b.' + key + ');'; + if (options[i] & SORT_DESCENDING) { + str += 'return x < y ? 1 : (x > y ? -1 : 0);'; + } else { + str += 'return x < y ? -1 : (x > y ? 1 : 0);'; + } + var numericComparator = numericComparatorCache[str]; + if (!numericComparator) { + numericComparator = numericComparatorCache[str] = new Function('a', 'b', str); + } + o.sort(numericComparator); + } else { + o.sort(function (a, b) { + return asCompare(a[key], b[key], options[i] | 0); + }); + } + } + return o; + }, + _indexOf: function _indexOf(o, searchElement, fromIndex) { + return o.indexOf(searchElement, fromIndex); + }, + _lastIndexOf: function _lastIndexOf(o, searchElement, fromIndex) { + return o.lastIndexOf(searchElement, fromIndex); + }, + _every: function _every(o, callback, thisObject) { + for (var i = 0; i < o.length; i++) { + if (callback.call(thisObject, o[i], i, o) !== true) { + return false; + } + } + return false; + }, + _filter: function _filter(o, callback, thisObject) { + var result = []; + for (var i = 0; i < o.length; i++) { + if (callback.call(thisObject, o[i], i, o) === true) { + result.push(o[i]); + } + } + return result; + }, + _forEach: function _forEach(o, callback, thisObject) { + return o.forEach(callback, thisObject); + }, + _map: function _map(o, callback, thisObject) { + return o.map(callback, thisObject); + }, + _some: function _some(o, callback, thisObject) { + return o.some(callback, thisObject); + } + }, + instance: { + pop: Array.prototype.pop, + push: Array.prototype.push, + unshift: Array.prototype.unshift, + length: { + get: function length() { + return this.length; + }, + set: function length(newLength) { + this.length = newLength; + } + } + } + }; + c.coerce = function (value) { + return value; + }; + c.isInstanceOf = function (value) { + return true; + }; + return c; +} var XMLClass, XMLListClass, QNameClass, ASXML, XML, ASXMLList, XMLList; var isXMLType, isXMLName, XMLParser; (function () { @@ -28393,7 +27392,6 @@ var isXMLType, isXMLName, XMLParser; if (n.children.length) { s += '>'; for (var i = 0; i < n.children.length; i++) { - s += '\n'; s += visit(n.children[i], this); } s += ''; @@ -28938,7 +27936,7 @@ var isXMLType, isXMLName, XMLParser; } this.init(kind, uri, name, prefix); }; - var c = new Class('XML', ASXML, Domain.passthroughCallable(ASXML)); + var c = new Class('XML', ASXML, ApplicationDomain.passthroughCallable(ASXML)); c.flags = FLAG_IGNORE_COMMENTS | FLAG_IGNORE_PROCESSING_INSTRUCTIONS | FLAG_IGNORE_WHITESPACE | FLAG_PRETTY_PRINTING; c.prettyIndent = 2; c.extend(baseClass); @@ -29148,11 +28146,15 @@ var isXMLType, isXMLName, XMLParser; }); } } else { - return this.children.some(function (v, i) { - if ((anyName || v.kind === 'element' && v.name.localName === name.localName) && (anyNamespace || v.kind === 'element' && v.name.uri === name.uri)) { - return true; - } - }); + if (this.children.some(function (v, i) { + if ((anyName || v.kind === 'element' && v.name.localName === name.localName) && (anyNamespace || v.kind === 'element' && v.name.uri === name.uri)) { + return true; + } + })) { + return true; + } + var resolved = Multiname.isQName(mn) ? mn : resolveMultiname(this, mn); + return !(!this[Multiname.getQualifiedName(resolved)]); } }; Xp.delete = function (key, isMethod) { @@ -29385,7 +28387,8 @@ var isXMLType, isXMLName, XMLParser; return toString(this); }, hasOwnProperty: function hasOwnProperty(P) { - notImplemented('XML.hasOwnProperty'); + somewhatImplemented('XML.hasOwnProperty'); + return this.hasProperty(P); }, propertyIsEnumerable: function propertyIsEnumerable(P) { notImplemented('XML.propertyIsEnumerable'); @@ -29581,7 +28584,7 @@ var isXMLType, isXMLName, XMLParser; this.targetProperty = targetProperty ? targetProperty : null; this.children = []; }; - var c = new Class('XMLList', ASXMLList, Domain.passthroughCallable(ASXMLList)); + var c = new Class('XMLList', ASXMLList, ApplicationDomain.passthroughCallable(ASXMLList)); c.extend(baseClass); var XLp = XMLList.prototype = ASXMLList.prototype; XLp.canHandleProperties = true; @@ -29875,7 +28878,8 @@ var isXMLType, isXMLName, XMLParser; return toString(this); }, hasOwnProperty: function hasOwnProperty(P) { - notImplemented('XMLList.hasOwnProperty'); + somewhatImplemented('XMLList.hasOwnProperty'); + return this.hasProperty(P); }, propertyIsEnumerable: function propertyIsEnumerable(P) { notImplemented('XMLList.propertyIsEnumerable'); @@ -30089,7 +29093,7 @@ var isXMLType, isXMLName, XMLParser; this.isAnyNamespace = mn.isAnyNamespace(); this.isAttr = mn.isAttribute(); }; - var c = new Class('QName', QName, Domain.passthroughCallable(QName)); + var c = new Class('QName', QName, ApplicationDomain.passthroughCallable(QName)); c.extend(baseClass); QNp = QName.prototype; defineNonEnumerableGetter(QNp, 'localName', function () { @@ -30733,7 +29737,7 @@ function ProxyClass(runtime, scope, instanceConstructor, baseClass) { function ProxyConstructor() { somewhatImplemented('Proxy'); } - var c = new Class('Proxy', ProxyConstructor, Domain.coerceCallable(ProxyConstructor)); + var c = new Class('Proxy', ProxyConstructor, ApplicationDomain.coerceCallable(ProxyConstructor)); c.extendBuiltin(baseClass); return c; } @@ -30888,7 +29892,7 @@ function DictionaryClass(domain, scope, instanceConstructor, baseClass) { } this.primitiveMap = createEmptyObject(); } - var c = new Class('Dictionary', ASDictionary, Domain.passthroughCallable(ASDictionary)); + var c = new Class('Dictionary', ASDictionary, ApplicationDomain.passthroughCallable(ASDictionary)); c.extendNative(baseClass, ASDictionary); function makePrimitiveKey(key) { if (typeof key === 'string' || typeof key === 'number') { @@ -30946,6 +29950,9 @@ function DictionaryClass(domain, scope, instanceConstructor, baseClass) { for (var k in this.primitiveMap) { primitiveMapKeys.push(k); } + if (this.weakKeys) { + return primitiveMapKeys; + } return primitiveMapKeys.concat(this.keys); }); c.native = { @@ -30962,9 +29969,9 @@ function debugBreak(message) { } var ASNamespace; var natives = function () { - var C = Domain.passthroughCallable; - var CC = Domain.constructingCallable; - function ObjectClass(runtime, scope, instanceConstructor, baseClass) { + var C = ApplicationDomain.passthroughCallable; + var CC = ApplicationDomain.constructingCallable; + function ObjectClass(applicationDomain, scope, instanceConstructor, baseClass) { var c = new Class('Object', Object, C(Object)); c.native = { instance: { @@ -31119,9 +30126,19 @@ var natives = function () { concat: Sp.concat, localeCompare: Sp.localeCompare, match: function (re) { - if (re === void 0) { + if (re === void 0 || re === null) { return null; } else { + if (re instanceof RegExp && re.global) { + var matches = [], m; + while (m = re.exec(this)) { + matches.push(m[0]); + } + return matches; + } + if (!(re instanceof RegExp) && !(typeof re === 'string')) { + re = String(re); + } return this.match(re); } }, @@ -31171,101 +30188,6 @@ var natives = function () { }; return c; } - function ArrayClass(runtime, scope, instanceConstructor, baseClass) { - var c = new Class('Array', Array, C(Array)); - c.extendBuiltin(baseClass); - var Ap = Array.prototype; - var CACHE_NUMERIC_COMPARATORS = true; - var numericComparatorCache = createEmptyObject(); - c.native = { - instance: { - length: { - get: function () { - return this.length; - }, - set: function (l) { - this.length = l; - } - }, - join: Ap.join, - pop: Ap.pop, - push: Ap.push, - reverse: Ap.reverse, - concat: Ap.concat, - shift: Ap.shift, - slice: Ap.slice, - unshift: Ap.unshift, - splice: Ap.splice, - indexOf: Ap.indexOf, - lastIndexOf: Ap.lastIndexOf, - every: Ap.every, - filter: Ap.filter, - forEach: Ap.forEach, - map: Ap.map, - some: Ap.some - }, - static: { - _sortOn: function (o, names, options) { - if (isString(names)) { - names = [ - names - ]; - } - if (isNumber(options)) { - options = [ - options - ]; - } - for (var i = names.length - 1; i >= 0; i--) { - var key = Multiname.getPublicQualifiedName(names[i]); - if (CACHE_NUMERIC_COMPARATORS && options[i] & SORT_NUMERIC) { - var str = 'var x = toNumber(a.' + key + '), y = toNumber(b.' + key + ');'; - if (options[i] & SORT_DESCENDING) { - str += 'return x < y ? 1 : (x > y ? -1 : 0);'; - } else { - str += 'return x < y ? -1 : (x > y ? 1 : 0);'; - } - var numericComparator = numericComparatorCache[str]; - if (!numericComparator) { - numericComparator = numericComparatorCache[str] = new Function('a', 'b', str); - } - o.sort(numericComparator); - } else { - o.sort(function (a, b) { - return asCompare(a[key], b[key], options[i] | 0); - }); - } - } - return o; - }, - _sort: function (o, args) { - if (args.length === 0) { - return o.sort(); - } - var compareFunction, options = 0; - if (args[0] instanceof Function) { - compareFunction = args[0]; - } else if (isNumber(args[0])) { - options = args[0]; - } - if (isNumber(args[1])) { - options = args[1]; - } - o.sort(function (a, b) { - return asCompare(a, b, options, compareFunction); - }); - return o; - } - } - }; - c.coerce = function (value) { - return value; - }; - c.isInstanceOf = function (value) { - return true; - }; - return c; - } function VectorClass(domain, scope, instanceConstructor, baseClass) { return createVectorClass(undefined, baseClass); } @@ -31317,10 +30239,10 @@ var natives = function () { instance: { fixed: { get: function () { - return this.fixed; + return this._fixed; }, set: function (v) { - this.fixed = v; + this._fixed = v; } }, length: { @@ -31336,12 +30258,8 @@ var natives = function () { shift: Vp.shift, unshift: Vp.unshift, _reverse: Vp.reverse, - _every: Vp.every, _filter: Vp.filter, - _forEach: Vp.forEach, _map: Vp.map, - _some: Vp.some, - _sort: Vp.sort, newThisType: function newThisType() { return new cls.instanceConstructor(); }, @@ -31350,12 +30268,16 @@ var natives = function () { } }, static: { + _some: function (o, callback, thisObject) { + return o.some(callback, thisObject); + }, _every: function (o, callback, thisObject) { return o.every(callback, thisObject); }, _forEach: function (o, callback, thisObject) { return o.forEach(callback, thisObject); - } + }, + _sort: arraySort } }; cls.vectorType = type; @@ -31743,6 +30665,9 @@ var natives = function () { var INITIAL_SIZE = 128; var defaultObjectEncoding = 3; function ByteArray(bytes) { + if (bytes instanceof ByteArray) { + return bytes; + } var initData = bytes || this.symbol && this.symbol.data; if (initData) { this.a = new ArrayBuffer(initData.length); @@ -32069,7 +30994,7 @@ var natives = function () { instance: { init: function (base) { this.base = base; - this.nativeObject = new Domain(avm2, base ? base.nativeObject : null); + this.nativeObject = new ApplicationDomain(avm2, base ? base.nativeObject : null); }, loadBytes: function (byteArray, swfVersion) { this.nativeObject.executeAbc(new AbcFile(byteArray.readRawBytes())); @@ -32090,10 +31015,43 @@ var natives = function () { }; return c; } + function SystemClass(runtime, scope, instanceConstructor, baseClass) { + var c = new Class('System', instanceConstructor, C(instanceConstructor)); + c.extend(baseClass); + c.native = { + static: { + swfVersion: { + get: function () { + return 19; + } + }, + apiVersion: { + get: function () { + return 26; + } + }, + getArgv: function () { + return []; + }, + getRunmode: function () { + return 'mixed'; + } + } + }; + return c; + } + function bugzilla(n) { + switch (n) { + case 574600: + return true; + } + return false; + } return { print: constant(print), notImplemented: constant(notImplemented), debugBreak: constant(debugBreak), + bugzilla: constant(bugzilla), decodeURI: constant(decodeURI), decodeURIComponent: constant(decodeURIComponent), encodeURI: constant(encodeURI), @@ -32158,6 +31116,7 @@ var natives = function () { CapabilitiesClass: CapabilitiesClass, FileClass: FileClass, DomainClass: DomainClass, + SystemClass: SystemClass, getQualifiedClassName: constant(function (value) { if (value === null) { return 'null'; @@ -32229,7 +31188,7 @@ var natives = function () { return notImplemented(value + ' (superOf ' + typeof value + ')'); }), getDefinitionByName: constant(function (name) { - var simpleName = name.replace('::', '.'); + var simpleName = String(name).replace('::', '.'); return AVM2.currentDomain().getClass(simpleName); }), describeTypeJSON: constant(describeTypeJSON), @@ -32743,7 +31702,6 @@ var SourceTracer = function () { } var getSet = trait.isGetter() ? 'get ' : trait.isSetter() ? 'set ' : ''; if (!mi.isNative()) { - print('XXX: ' + prefix + getSet + trait.name.getName() + ' ()'); } } if (mi.isNative()) { @@ -32779,7 +31737,7 @@ var SourceTracer = function () { writer.writeLn('Cut and paste the following into `native.js\' and edit accordingly'); writer.writeLn('8< --------------------------------------------------------------'); writer.enter('natives.' + native.cls + ' = function ' + native.cls + '(runtime, scope, instanceConstructor, baseClass) {'); - writer.writeLn('var c = new Class("' + name + '", instanceConstructor, Domain.passthroughCallable(instanceConstructor));'); + writer.writeLn('var c = new Class("' + name + '", instanceConstructor, ApplicationDomain.passthroughCallable(instanceConstructor));'); writer.writeLn('c.extend(baseClass);\n'); function traceTraits(traits, isStatic) { var nativeMethodTraits = []; @@ -32923,12 +31881,7 @@ var SourceTracer = function () { writer.leave('}'); writer.leave('},'); writer.enter('script: {'); - writer.enter('static: {'); - writer.writeLn('// ...'); - writer.leave('},'); - writer.enter('instance: {'); - writeTraits(ii.traits, false); - writer.leave('}'); + writer.writeLn('instance: Glue.ALL'); writer.leave('}'); writer.leave('}'); writer.leave('};'); @@ -33142,27 +32095,6 @@ function traceStatistics(writer, abc) { var Interpreter = new (function () { function Interpreter() { } - function popName(stack, mn) { - if (Multiname.isRuntime(mn)) { - var namespaces = mn.namespaces, name = mn.name; - var flags = mn.flags & Multiname.ATTRIBUTE; - if (Multiname.isRuntimeName(mn)) { - name = stack.pop(); - } - if (isNumeric(name) || isObject(name)) { - true; - return name; - } - if (Multiname.isRuntimeNamespace(mn)) { - namespaces = [ - stack.pop() - ]; - } - mn = new Multiname(namespaces, name, flags); - } - true; - return mn; - } function popNameInto(stack, mn, out) { out.flags = mn.flags; if (mn.isRuntimeName()) { @@ -33189,7 +32121,7 @@ var Interpreter = new (function () { var strings = abc.constantPool.strings; var methods = abc.methods; var multinames = abc.constantPool.multinames; - var domain = abc.domain; + var domain = abc.applicationDomain; var exceptions = method.exceptions; var locals = [ $this @@ -33215,9 +32147,8 @@ var Interpreter = new (function () { } else if (method.needsArguments()) { locals.push(sliceArguments(methodArgs, 0)); } - var obj, index, multiname, res, a, b, args = [], name; - var tmpMultiname = Multiname.TEMPORARY; var bytecodes = method.analysis.bytecodes; + var object, index, multiname, result, a, b, args = [], mn = Multiname.TEMPORARY; interpret: for (var pc = 0, end = bytecodes.length; pc < end;) { try { @@ -33227,13 +32158,13 @@ var Interpreter = new (function () { case 3: throw stack.pop(); case 4: - name = popName(stack, multinames[bc.index]); - stack[stack.length - 1] = getSuper(savedScope, stack[stack.length - 1], name); + popNameInto(stack, multinames[bc.index], mn); + stack.push(stack.pop().asGetSuper(savedScope, mn.namespaces, mn.name, mn.flags)); break; case 5: value = stack.pop(); - name = popName(stack, multinames[bc.index]); - setSuper(savedScope, stack.pop(), name, value); + popNameInto(stack, multinames[bc.index], mn); + stack.pop().asSetSuper(savedScope, mn.namespaces, mn.name, mn.flags, value); break; case 8: locals[bc.index] = undefined; @@ -33329,10 +32260,10 @@ var Interpreter = new (function () { stack[stack.length - 1] = boxValue(stack[stack.length - 1]).asNextValue(index); break; case 50: - res = asHasNext2(locals[bc.object], locals[bc.index]); - locals[bc.object] = res.object; - locals[bc.index] = res.index; - stack.push(!(!res.index)); + result = asHasNext2(locals[bc.object], locals[bc.index]); + locals[bc.object] = result.object; + locals[bc.index] = result.index; + stack.push(!(!result.index)); break; case 32: stack.push(null); @@ -33372,9 +32303,9 @@ var Interpreter = new (function () { stack.push(stack[stack.length - 1]); break; case 43: - obj = stack[stack.length - 1]; + object = stack[stack.length - 1]; stack[stack.length - 1] = stack[stack.length - 2]; - stack[stack.length - 2] = obj; + stack[stack.length - 2] = object; break; case 48: scopeStack.push(boxValue(stack.pop())); @@ -33384,19 +32315,13 @@ var Interpreter = new (function () { break; case 65: popManyInto(stack, bc.argCount, args); - obj = stack.pop(); - stack[stack.length - 1] = stack[stack.length - 1].apply(obj, args); + object = stack.pop(); + stack[stack.length - 1] = stack[stack.length - 1].apply(object, args); break; case 66: popManyInto(stack, bc.argCount, args); stack[stack.length - 1] = construct(stack[stack.length - 1], args); break; - case 69: - popManyInto(stack, bc.argCount, args); - name = popName(stack, multinames[bc.index]); - obj = stack[stack.length - 1]; - stack[stack.length - 1] = getSuper(savedScope, obj, name).apply(obj, args); - break; case 71: return; case 72: @@ -33406,15 +32331,15 @@ var Interpreter = new (function () { return stack.pop(); case 73: popManyInto(stack, bc.argCount, args); - obj = stack.pop(); - savedScope.object.baseClass.instanceConstructorNoInitialize.apply(obj, args); + object = stack.pop(); + savedScope.object.baseClass.instanceConstructorNoInitialize.apply(object, args); break; case 74: popManyInto(stack, bc.argCount, args); - popNameInto(stack, multinames[bc.index], tmpMultiname); - obj = boxValue(stack[stack.length - 1]); - obj = obj.asConstructProperty(tmpMultiname.namespaces, tmpMultiname.name, tmpMultiname.flags, args); - stack[stack.length - 1] = obj; + popNameInto(stack, multinames[bc.index], mn); + object = boxValue(stack[stack.length - 1]); + object = object.asConstructProperty(mn.namespaces, mn.name, mn.flags, args); + stack[stack.length - 1] = object; break; case 75: notImplemented(); @@ -33423,35 +32348,38 @@ var Interpreter = new (function () { case 70: case 79: popManyInto(stack, bc.argCount, args); - popNameInto(stack, multinames[bc.index], tmpMultiname); - res = boxValue(stack.pop()).asCallProperty(tmpMultiname.namespaces, tmpMultiname.name, tmpMultiname.flags, op === OP_callproplex, args); + popNameInto(stack, multinames[bc.index], mn); + result = boxValue(stack.pop()).asCallProperty(mn.namespaces, mn.name, mn.flags, op === OP_callproplex, args); if (op !== OP_callpropvoid) { - stack.push(res); + stack.push(result); } break; + case 69: case 78: popManyInto(stack, bc.argCount, args); - name = popName(stack, multinames[bc.index]); - obj = stack.pop(); - getSuper(savedScope, obj, name).apply(obj, args); + popNameInto(stack, multinames[bc.index], mn); + result = stack.pop().asCallSuper(savedScope, mn.namespaces, mn.name, mn.flags, args); + if (op !== OP_callsupervoid) { + stack.push(result); + } break; case 83: popManyInto(stack, bc.argCount, args); stack[stack.length - 1] = applyType(domain, stack[stack.length - 1], args); break; case 85: - obj = {}; + object = {}; for (var i = 0; i < bc.argCount; i++) { value = stack.pop(); - obj[Multiname.getPublicQualifiedName(stack.pop())] = value; + object[Multiname.getPublicQualifiedName(stack.pop())] = value; } - stack.push(obj); + stack.push(object); break; case 86: - obj = []; + object = []; popManyInto(stack, bc.argCount, args); - obj.push.apply(obj, args); - stack.push(obj); + object.push.apply(object, args); + stack.push(object); break; case 87: true; @@ -33461,8 +32389,8 @@ var Interpreter = new (function () { stack[stack.length - 1] = createClass(abc.classes[bc.index], stack[stack.length - 1], scopeStack.topScope()); break; case 89: - name = popName(stack, multinames[bc.index]); - stack.push(getDescendants(stack.pop(), name)); + popNameInto(stack, multinames[bc.index], mn); + stack.push(getDescendants(stack.pop(), mn)); break; case 90: true; @@ -33470,18 +32398,19 @@ var Interpreter = new (function () { break; case 94: case 93: - popNameInto(stack, multinames[bc.index], tmpMultiname); - stack.push(scopeStack.topScope().findScopeProperty(tmpMultiname.namespaces, tmpMultiname.name, tmpMultiname.flags, domain, op === OP_findpropstrict)); + popNameInto(stack, multinames[bc.index], mn); + stack.push(scopeStack.topScope().findScopeProperty(mn.namespaces, mn.name, mn.flags, domain, op === OP_findpropstrict)); break; case 96: multiname = multinames[bc.index]; - stack.push(scopeStack.topScope().findScopeProperty(multiname.namespaces, multiname.name, multiname.flags, domain, true).asGetProperty(multiname.namespaces, multiname.name, multiname.flags)); + object = scopeStack.topScope().findScopeProperty(multiname.namespaces, multiname.name, multiname.flags, domain, true); + stack.push(object.asGetProperty(multiname.namespaces, multiname.name, multiname.flags)); break; case 104: case 97: value = stack.pop(); - popNameInto(stack, multinames[bc.index], tmpMultiname); - boxValue(stack.pop()).asSetProperty(tmpMultiname.namespaces, tmpMultiname.name, tmpMultiname.flags, value); + popNameInto(stack, multinames[bc.index], mn); + boxValue(stack.pop()).asSetProperty(mn.namespaces, mn.name, mn.flags, value); break; case 98: stack.push(locals[bc.index]); @@ -33496,20 +32425,20 @@ var Interpreter = new (function () { stack.push(scopeStack.get(bc.index)); break; case 102: - popNameInto(stack, multinames[bc.index], tmpMultiname); - stack[stack.length - 1] = boxValue(stack[stack.length - 1]).asGetProperty(tmpMultiname.namespaces, tmpMultiname.name, tmpMultiname.flags); + popNameInto(stack, multinames[bc.index], mn); + stack[stack.length - 1] = boxValue(stack[stack.length - 1]).asGetProperty(mn.namespaces, mn.name, mn.flags); break; case 106: - popNameInto(stack, multinames[bc.index], tmpMultiname); - stack[stack.length - 1] = boxValue(stack[stack.length - 1]).asDeleteProperty(tmpMultiname.namespaces, tmpMultiname.name, tmpMultiname.flags); + popNameInto(stack, multinames[bc.index], mn); + stack[stack.length - 1] = boxValue(stack[stack.length - 1]).asDeleteProperty(mn.namespaces, mn.name, mn.flags); break; case 108: stack[stack.length - 1] = asGetSlot(stack[stack.length - 1], bc.index); break; case 109: value = stack.pop(); - obj = stack.pop(); - asSetSlot(obj, bc.index, value); + object = stack.pop(); + asSetSlot(object, bc.index, value); break; case 112: stack[stack.length - 1] = stack[stack.length - 1] + ''; @@ -33545,8 +32474,8 @@ var Interpreter = new (function () { stack[stack.length - 2] = asAsType(stack.pop(), stack[stack.length - 1]); break; case 137: - obj = stack[stack.length - 1]; - stack[stack.length - 1] = obj == undefined ? null : obj; + object = stack[stack.length - 1]; + stack[stack.length - 1] = object == undefined ? null : object; break; case 144: stack[stack.length - 1] = -stack[stack.length - 1]; @@ -33703,8 +32632,8 @@ var Interpreter = new (function () { }())(); var AVM2 = function () { function avm2(sysMode, appMode, findDefiningAbc, loadAVM1) { - this.systemDomain = new Domain(this, null, sysMode, true); - this.applicationDomain = new Domain(this, this.systemDomain, appMode, false); + this.systemDomain = new ApplicationDomain(this, null, sysMode, true); + this.applicationDomain = new ApplicationDomain(this, this.systemDomain, appMode, false); this.findDefiningAbc = findDefiningAbc; this.loadAVM1 = loadAVM1; this.isAVM1Loaded = false; @@ -33719,7 +32648,7 @@ var AVM2 = function () { for (var i = 0; i < maxDepth && caller; i++) { var mi = caller.methodInfo; if (mi) { - domain = mi.abc.domain; + domain = mi.abc.applicationDomain; break; } caller = caller.caller; @@ -37196,305 +36125,12 @@ function createAVM2(builtinPath, libraryPath, avm1Path, sysMode, appMode, next) avm2.builtinsLoaded = true; console.info(JSON.stringify(Counter.toJSON())); console.timeEnd('Load AVM2'); - addProfileMarker('End Load AVM2'); next(avm2); } } -function scriptProperties(namespace, props) { - return props.reduce(function (o, p) { - o[p] = namespace + ' ' + p; - return o; - }, {}); -} -function cloneObject(obj) { - var clone = Object.create(null); - for (var prop in obj) - clone[prop] = obj[prop]; - return clone; -} -function throwError(name, error) { - var message = formatErrorMessage.apply(null, slice.call(arguments, 1)); - throwErrorFromVM(AVM2.currentDomain(), name, message, error.code); -} -function sortByDepth(a, b) { - var levelA = a._level; - var levelB = b._level; - if (a._parent !== b._parent && a._index > -1 && b._index > -1) { - while (a._level > levelB) { - a = a._parent; - } - while (b._level > levelA) { - b = b._parent; - } - while (a._level > 1) { - if (a._parent === b._parent) { - break; - } - a = a._parent; - b = b._parent; - } - } - if (a === b) { - return levelA - levelB; - } - return a._index - b._index; -} -function sortNumeric(a, b) { - return a - b; -} -var Promise = function PromiseClosure() { - function isPromise(obj) { - return typeof obj === 'object' && obj !== null && typeof obj.then === 'function'; - } - function defaultOnFulfilled(value) { - return value; - } - function defaultOnRejected(reason) { - throw reason; - } - function propagateFulfilled(subject, value) { - subject.subpromisesValue = value; - var subpromises = subject.subpromises; - if (!subpromises) { - return; - } - for (var i = 0; i < subpromises.length; i++) { - subpromises[i].fulfill(value); - } - delete subject.subpromises; - } - function propagateRejected(subject, reason) { - subject.subpromisesReason = reason; - var subpromises = subject.subpromises; - if (!subpromises) { - if (!true) { - console.warn(reason); - } - return; - } - for (var i = 0; i < subpromises.length; i++) { - subpromises[i].reject(reason); - } - delete subject.subpromises; - } - function performCall(callback, arg, subject) { - try { - var value = callback(arg); - if (isPromise(value)) { - value.then(function Promise_queueCall_onFulfilled(value) { - propagateFulfilled(subject, value); - }, function Promise_queueCall_onRejected(reason) { - propagateRejected(subject, reason); - }); - return; - } - propagateFulfilled(subject, value); - } catch (ex) { - propagateRejected(subject, ex); - } - } - var queue = []; - function processQueue() { - while (queue.length > 0) { - var task = queue[0]; - if (task.directCallback) { - task.callback.call(task.subject, task.arg); - } else { - performCall(task.callback, task.arg, task.subject); - } - queue.shift(); - } - } - function queueCall(callback, arg, subject, directCallback) { - if (queue.length === 0) { - setTimeout(processQueue, 0); - } - queue.push({ - callback: callback, - arg: arg, - subject: subject, - directCallback: directCallback - }); - } - function Promise(onFulfilled, onRejected) { - this.state = 'pending'; - this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : defaultOnFulfilled; - this.onRejected = typeof onRejected === 'function' ? onRejected : defaultOnRejected; - } - Promise.prototype = { - fulfill: function Promise_resolve(value) { - if (this.state !== 'pending') { - return; - } - this.state = 'fulfilled'; - this.value = value; - queueCall(this.onFulfilled, value, this, false); - }, - reject: function Promise_reject(reason) { - if (this.state !== 'pending') { - return; - } - this.state = 'rejected'; - this.reason = reason; - queueCall(this.onRejected, reason, this, false); - }, - then: function Promise_then(onFulfilled, onRejected) { - var promise = new Promise(onFulfilled, onRejected); - if ('subpromisesValue' in this) { - queueCall(promise.fulfill, this.subpromisesValue, promise, true); - } else if ('subpromisesReason' in this) { - queueCall(promise.reject, this.subpromisesReason, promise, true); - } else { - var subpromises = this.subpromises || (this.subpromises = []); - subpromises.push(promise); - } - return promise; - }, - get resolved() { - return this.state === 'fulfilled'; - }, - resolve: function (value) { - this.fulfill(value); - } - }; - Promise.when = function Promise_when() { - var promise = new Promise(); - if (arguments.length === 0) { - promise.resolve(); - return promise; - } - var promises = slice.call(arguments, 0); - var result = []; - var i = 1; - function fulfill(value) { - result.push(value); - if (i < promises.length) { - promises[i++].then(fulfill, reject); - } else { - promise.resolve(result); - } - return value; - } - function reject(reason) { - promise.reject(reason); - } - promises[0].then(fulfill, reject); - return promise; - }; - return Promise; - }(); -var QuadTree = function (x, y, width, height, level) { - this.x = x | 0; - this.y = y | 0; - this.width = width | 0; - this.height = height | 0; - this.level = level | 0; - this.stuckObjects = []; - this.objects = []; - this.nodes = []; -}; -QuadTree.prototype._findIndex = function (xMin, yMin, xMax, yMax) { - var midX = this.x + (this.width / 2 | 0); - var midY = this.y + (this.height / 2 | 0); - var top = yMin < midY && yMax < midY; - var bottom = yMin > midY; - if (xMin < midX && xMax < midX) { - if (top) { - return 1; - } else if (bottom) { - return 2; - } - } else if (xMin > midX) { - if (top) { - return 0; - } else if (bottom) { - return 3; - } - } - return -1; -}; -QuadTree.prototype.insert = function (obj) { - var nodes = this.nodes; - if (nodes.length) { - var index = this._findIndex(obj.xMin, obj.yMin, obj.xMax, obj.yMax); - if (index > -1) { - nodes[index].insert(obj); - } else { - this.stuckObjects.push(obj); - obj._qtree = this; - } - return; - } - var objects = this.objects; - objects.push(obj); - if (objects.length > 4 && this.level < 10) { - this._subdivide(); - while (objects.length) { - this.insert(objects.shift()); - } - return; - } - obj._qtree = this; -}; -QuadTree.prototype.delete = function (obj) { - if (obj._qtree !== this) { - return; - } - var index = this.objects.indexOf(obj); - if (index > -1) { - this.objects.splice(index, 1); - } else { - index = this.stuckObjects.indexOf(obj); - this.stuckObjects.splice(index, 1); - } - obj._qtree = null; -}; -QuadTree.prototype._stack = []; -QuadTree.prototype._out = []; -QuadTree.prototype.retrieve = function (xMin, yMin, xMax, yMax) { - var stack = this._stack; - var out = this._out; - out.length = 0; - var node = this; - do { - if (node.nodes.length) { - var index = node._findIndex(xMin, yMin, xMax, yMax); - if (index > -1) { - stack.push(node.nodes[index]); - } else { - stack.push.apply(stack, node.nodes); - } - } - out.push.apply(out, node.stuckObjects); - out.push.apply(out, node.objects); - node = stack.pop(); - } while (node); - return out; -}; -QuadTree.prototype._subdivide = function () { - var halfWidth = this.width / 2 | 0; - var halfHeight = this.height / 2 | 0; - var midX = this.x + halfWidth; - var midY = this.y + halfHeight; - var level = this.level + 1; - this.nodes[0] = new QuadTree(midX, this.y, halfWidth, halfHeight, level); - this.nodes[1] = new QuadTree(this.x, this.y, halfWidth, halfHeight, level); - this.nodes[2] = new QuadTree(this.x, midY, halfWidth, halfHeight, level); - this.nodes[3] = new QuadTree(midX, midY, halfWidth, halfHeight, level); -}; -var EXTERNAL_INTERFACE_FEATURE = 1; -var CLIPBOARD_FEATURE = 2; -var SHAREDOBJECT_FEATURE = 3; -var VIDEO_FEATURE = 4; -var SOUND_FEATURE = 5; -var NETCONNECTION_FEATURE = 6; -if (!this.performance) { - this.performance = {}; -} -if (!this.performance.now) { - this.performance.now = Date.now; -} { + var MAX_SNAP_DRAW_SCALE_TO_CACHE = 8; + var CACHE_SNAP_DRAW_AFTER = 3; var BitmapDefinition = function () { function setBitmapData(value) { if (this._bitmapData) { @@ -37520,7 +36156,9 @@ if (!this.performance.now) { yMax: 0 }; } - this._invalidate(); + this._drawableChanged(); + this._invalidateBounds(); + this._invalidateTransform(); } return { __class__: 'flash.display.Bitmap', @@ -37528,21 +36166,85 @@ if (!this.performance.now) { if (!this._bitmapData) { return; } + var scaledImage; ctx.save(); if (this._pixelSnapping === 'auto' || this._pixelSnapping === 'always') { var transform = this._getConcatenatedTransform(true); var EPSILON = 0.001; - if (Math.abs(Math.abs(transform.a) - 1) <= EPSILON && Math.abs(Math.abs(transform.d) - 1) <= EPSILON && Math.abs(transform.b) <= EPSILON && Math.abs(transform.c) <= EPSILON) { + var aInt = Math.abs(Math.round(transform.a)); + var dInt = Math.abs(Math.round(transform.d)); + var snapPixels; + if (aInt >= 1 && aInt <= MAX_SNAP_DRAW_SCALE_TO_CACHE && dInt >= 1 && dInt <= MAX_SNAP_DRAW_SCALE_TO_CACHE && Math.abs(Math.abs(transform.a) / aInt - 1) <= EPSILON && Math.abs(Math.abs(transform.d) / dInt - 1) <= EPSILON && Math.abs(transform.b) <= EPSILON && Math.abs(transform.c) <= EPSILON) { + if (aInt === 1 && dInt === 1) { + snapPixels = true; + } else { + var sizeKey = aInt + 'x' + dInt; + if (this._snapImageCache.size !== sizeKey) { + this._snapImageCache.size = sizeKey; + this._snapImageCache.hits = 0; + this._snapImageCache.image = null; + } + if (++this._snapImageCache.hits === CACHE_SNAP_DRAW_AFTER) { + this._cacheSnapImage(sizeKey, aInt, dInt); + } + scaledImage = this._snapImageCache.image; + snapPixels = !(!scaledImage); + } + } else { + snapPixels = false; + } + if (snapPixels) { ctx.setTransform(transform.a < 0 ? -1 : 1, 0, 0, transform.d < 0 ? -1 : 1, transform.tx / 20 | 0, transform.ty / 20 | 0); } } colorTransform.setAlpha(ctx, true); ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = this._smoothing; - ctx.drawImage(this._bitmapData._drawable, 0, 0); + ctx.drawImage(scaledImage || this._bitmapData._getDrawable(), 0, 0); ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = false; ctx.restore(); traceRenderer.value && frameWriter.writeLn('Bitmap.draw() snapping: ' + this._pixelSnapping + ', dimensions: ' + this._bitmapData._drawable.width + ' x ' + this._bitmapData._drawable.height); - FrameCounter.count('Bitmap.draw()'); + }, + _drawableChanged: function () { + this._invalidate(); + this._snapImageCache.image = null; + this._snapImageCache.hints = 0; + }, + _cacheSnapImage: function (sizeKey, xScale, yScale) { + Counter.count('Cache scaled image'); + var original = this._bitmapData._getDrawable(); + var canvas = document.createElement('canvas'); + canvas.width = xScale * original.width; + canvas.height = yScale * original.height; + var ctx = canvas.getContext('2d'); + ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = this._smoothing; + ctx.drawImage(original, 0, 0, original.width, original.height, 0, 0, canvas.width, canvas.height); + var cache = this._snapImageCache; + var image = document.createElement('img'); + cache._tmp = [ + canvas, + image + ]; + if ('toBlob' in canvas) { + canvas.toBlob(function (blob) { + if (cache.size !== sizeKey) { + return; + } + image.onload = function () { + URL.revokeObjectURL(blob); + if (cache.size === sizeKey) { + cache.image = image; + } + }; + image.src = URL.createObjectURL(blob); + }); + } else { + image.onload = function () { + if (cache.size === sizeKey) { + cache.image = image; + } + }; + image.src = canvas.toDataURL(); + } }, initialize: function () { }, @@ -37557,9 +36259,14 @@ if (!this.performance.now) { this._pixelSnapping = 'auto'; } this._smoothing = !(!smoothing); + this._snapImageCache = { + hits: 0, + size: '', + image: null + }; if (!bitmapData && this.symbol) { var symbol = this.symbol; - bitmapData = new flash.display.BitmapData(symbol.width, symbol.height); + bitmapData = new flash.display.BitmapData(symbol.width, symbol.height, true, 0); bitmapData._ctx.imageSmoothingEnabled = this._smoothing; bitmapData._ctx.mozImageSmoothingEnabled = this._smoothing; bitmapData._ctx.drawImage(symbol.img, 0, 0); @@ -37596,6 +36303,7 @@ if (!this.performance.now) { }; }.call(this); } +var CACHE_DRAWABLE_AFTER = 10; var BitmapDataDefinition = function () { function replaceRect(ctx, x, y, w, h, alpha) { if (alpha < 255) { @@ -37610,6 +36318,8 @@ var BitmapDataDefinition = function () { initialize: function () { this._changeNotificationTarget = null; this._locked = false; + this._requested = 0; + this._cache = null; if (this.symbol) { this._img = this.symbol.img; this._skipCopyToCanvas = this.symbol.skipCopyToCanvas; @@ -37670,7 +36380,7 @@ var BitmapDataDefinition = function () { if (flash.display.BitmapData.class.isInstanceOf(source)) { ctx.drawImage(source._drawable, 0, 0); } else { - new RenderVisitor(source, ctx, null, true).startFragment(); + new RenderVisitor(source, ctx, null, true).startFragment(matrix); } ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = false; ctx.restore(); @@ -37704,8 +36414,37 @@ var BitmapDataDefinition = function () { return; } if (this._changeNotificationTarget) { - this._changeNotificationTarget._invalidate(); + this._changeNotificationTarget._drawableChanged(); } + this._requested = 0; + this._cache = null; + }, + _getDrawable: function () { + if (this._img === this._drawable) { + return this._drawable; + } + this._requested++; + if (this._requested >= CACHE_DRAWABLE_AFTER) { + if (!this._cache) { + Counter.count('Cache drawable'); + var img = document.createElement('img'); + if ('toBlob' in this._drawable) { + this._drawable.toBlob(function (blob) { + img.src = URL.createObjectURL(blob); + img.onload = function () { + URL.revokeObjectURL(blob); + }; + }); + } else { + img.src = this._drawable.toDataURL(); + } + this._cache = img; + } + if (this._cache.width > 0) { + return this._cache; + } + } + return this._drawable; }, setPixel: function (x, y, color) { this.fillRect({ @@ -37831,19 +36570,23 @@ var DisplayObjectDefinition = function () { exitFrame: true, render: true }; - var p1 = { + var topLeft = { x: 0, y: 0 }; - var p2 = { + var topRight = { x: 0, y: 0 }; - var p3 = { + var bottomRight = { x: 0, y: 0 }; - var p4 = { + var bottomLeft = { + x: 0, + y: 0 + }; + var point = { x: 0, y: 0 }; @@ -37866,7 +36609,14 @@ var DisplayObjectDefinition = function () { this._cacheAsBitmap = false; this._children = []; this._clipDepth = null; - this._currentTransform = null; + this._currentTransform = { + a: 1, + b: 0, + c: 0, + d: 1, + tx: 0, + ty: 0 + }; this._concatenatedTransform = { a: 1, b: 0, @@ -37890,11 +36640,12 @@ var DisplayObjectDefinition = function () { this._owned = false; this._parent = null; this._rotation = 0; + this._rotationCos = 1; + this._rotationSin = 0; this._scale9Grid = null; this._scaleX = 1; this._scaleY = 1; this._stage = null; - this._transform = null; this._visible = true; this._hidden = false; this._wasCachedAsBitmap = false; @@ -37906,6 +36657,7 @@ var DisplayObjectDefinition = function () { this._level = -1; this._index = -1; this._depth = -1; + this._isContainer = false; blendModes = [ blendModeClass.NORMAL, blendModeClass.NORMAL, @@ -37947,35 +36699,9 @@ var DisplayObjectDefinition = function () { } var matrix = s.currentTransform; if (matrix) { - var a = matrix.a; - var b = matrix.b; - var c = matrix.c; - var d = matrix.d; - this._rotation = a !== 0 ? Math.atan(b / a) * 180 / Math.PI : b > 0 ? 90 : -90; - var sx = Math.sqrt(a * a + b * b); - this._scaleX = a > 0 ? sx : -sx; - var sy = Math.sqrt(d * d + c * c); - this._scaleY = d > 0 ? sy : -sy; - this._currentTransform = { - a: a, - b: b, - c: c, - d: d, - tx: matrix.tx, - ty: matrix.ty - }; + this._setTransformMatrix(matrix, false); } } - if (!this._currentTransform) { - this._currentTransform = { - a: 1, - b: 0, - c: 0, - d: 1, - tx: 0, - ty: 0 - }; - } this._accessibilityProperties = null; var self = this; this._onBroadcastMessage = function (type) { @@ -38013,8 +36739,16 @@ var DisplayObjectDefinition = function () { if (this === this._stage) { return toDeviceSpace ? this._concatenatedTransform : this._currentTransform; } + var invalidNode = null; var m, m2; - if (this._concatenatedTransform.invalid) { + var currentNode = this; + while (currentNode !== stage) { + if (currentNode._concatenatedTransform.invalid) { + invalidNode = currentNode; + } + currentNode = currentNode._parent; + } + if (invalidNode) { if (this._parent === stage) { m = this._concatenatedTransform; m2 = this._currentTransform; @@ -38025,23 +36759,20 @@ var DisplayObjectDefinition = function () { m.tx = m2.tx; m.ty = m2.ty; } else { - var stack = [ - this - ]; - var currentNode = this._parent; - while (currentNode !== stage) { - if (currentNode._concatenatedTransform.invalid) { - stack.push(currentNode); - } + var stack = []; + var currentNode = this; + while (currentNode !== invalidNode) { + stack.push(currentNode); currentNode = currentNode._parent; } - while (stack.length) { - var node = stack.pop(); + var node = invalidNode; + do { + var parent = node._parent; m = node._concatenatedTransform; m2 = node._currentTransform; - if (node._parent) { - if (node._parent !== this._stage) { - var m3 = node._parent._concatenatedTransform; + if (parent) { + if (parent !== stage) { + var m3 = parent._concatenatedTransform; m.a = m2.a * m3.a + m2.b * m3.c; m.b = m2.a * m3.b + m2.b * m3.d; m.c = m2.c * m3.a + m2.d * m3.c; @@ -38058,7 +36789,16 @@ var DisplayObjectDefinition = function () { m.ty = m2.ty; } m.invalid = false; - } + var nextNode = stack.pop(); + var children = node._children; + for (var i = 0; i < children.length; i++) { + var child = children[i]; + if (child !== nextNode) { + child._concatenatedTransform.invalid = true; + } + } + node = nextNode; + } while (node); } } else { m = this._concatenatedTransform; @@ -38076,45 +36816,81 @@ var DisplayObjectDefinition = function () { } return m; }, - _applyCurrentTransform: function (targetCoordSpace, point1, pointN) { - var m; - if (targetCoordSpace && targetCoordSpace !== this._parent) { - m = this._getConcatenatedTransform(); - } else { + _applyCurrentTransform: function (targetCoordSpace, pt1, ptN) { + if (!targetCoordSpace || targetCoordSpace === this) { + return; + } + var m, a, b, c, d, tx, ty; + if (targetCoordSpace === this._parent) { m = this._currentTransform; + a = m.a; + b = m.b; + c = m.c; + d = m.d; + tx = m.tx; + ty = m.ty; + } else { + m = this._getConcatenatedTransform(); + if (targetCoordSpace === this._stage) { + a = m.a; + b = m.b; + c = m.c; + d = m.d; + tx = m.tx; + ty = m.ty; + } else { + var m2 = targetCoordSpace._getConcatenatedTransform(); + var a2, b2, c2, d2, tx2, ty2; + if (m2.b || m2.c) { + var det = 1 / (m2.a * m2.d - m2.b * m2.c); + a2 = m2.d * det; + b2 = -m2.b * det; + c2 = -m2.c * det; + d2 = m2.a * det; + tx2 = -(a2 * m2.tx + c2 * m2.ty); + ty2 = -(b2 * m2.tx + d2 * m2.ty); + } else { + a2 = 1 / m2.a; + b2 = 0; + c2 = 0; + d2 = 1 / m2.d; + tx2 = m2.tx * -a2; + ty2 = m2.ty * -d2; + } + a = a2 * m.a + c2 * m.b; + b = b2 * m.a + d2 * m.b; + c = a2 * m.c + c2 * m.d; + d = b2 * m.c + d2 * m.d; + tx = a2 * m.tx + c2 * m.ty + tx2; + ty = b2 * m.tx + d2 * m.ty + ty2; + } + } + if (a === 1 && !b && !c && d === 1 && !tx && !ty) { + return; } for (var i = 1; i < arguments.length; i++) { - var point = arguments[i]; - var x = point.x; - var y = point.y; - point.x = Math.round(m.a * x + m.c * y + m.tx); - point.y = Math.round(m.d * y + m.b * x + m.ty); - } - if (m === this._concatenatedTransform) { - var fn = targetCoordSpace._applyCurrentInverseTransform; - fn.call.apply(fn, arguments); + var pt = arguments[i]; + var x = pt.x; + var y = pt.y; + pt.x = a * x + c * y + tx | 0; + pt.y = d * y + b * x + ty | 0; } }, - _applyCurrentInverseTransform: function (point1, pointN) { + _applyConcatenatedInverseTransform: function (pt) { var m = this._getConcatenatedTransform(); - var d = 1 / (m.a * m.d - m.b * m.c); - for (var i = 0; i < arguments.length; i++) { - var point = arguments[i]; - var x = point.x - m.tx; - var y = point.y - m.ty; - point.x = Math.round((m.d * x - m.c * y) * d); - point.y = Math.round((m.a * y - m.b * x) * d); - } + var det = 1 / (m.a * m.d - m.b * m.c); + var x = pt.x - m.tx; + var y = pt.y - m.ty; + pt.x = (m.d * x - m.c * y) * det | 0; + pt.y = (m.a * y - m.b * x) * det | 0; }, _hitTest: function (use_xy, x, y, useShape, hitTestObject) { if (use_xy) { - var pt = { - x: x, - y: y - }; - this._applyCurrentInverseTransform(pt); + point.x = x; + point.y = y; + this._applyConcatenatedInverseTransform(point); var b = this._getContentBounds(); - if (!(pt.x >= b.xMin && pt.x < b.xMax && pt.y >= b.yMin && pt.y < b.yMax)) { + if (!(point.x >= b.xMin && point.x < b.xMax && point.y >= b.yMin && point.y < b.yMax)) { return false; } if (!useShape || !this._graphics) { @@ -38124,7 +36900,7 @@ var DisplayObjectDefinition = function () { var subpaths = this._graphics._paths; for (var i = 0, n = subpaths.length; i < n; i++) { var path = subpaths[i]; - if (path.isPointInPath(pt.x, pt.y)) { + if (path.isPointInPath(point.x, point.y)) { return true; } if (path.strokeStyle) { @@ -38133,7 +36909,7 @@ var DisplayObjectDefinition = function () { strokePath = path.strokePath(path.drawingStyles); path._strokePath = strokePath; } - if (strokePath.isPointInPath(pt.x, pt.y)) { + if (strokePath.isPointInPath(point.x, point.y)) { return true; } } @@ -38148,8 +36924,8 @@ var DisplayObjectDefinition = function () { } return false; } - var b1 = this.getBounds(); - var b2 = hitTestObject.getBounds(); + var b1 = this.getBounds(this._stage); + var b2 = hitTestObject.getBounds(hitTestObject._stage); x = Math.max(b1.xMin, b2.xMin); y = Math.max(b1.yMin, b2.yMin); var width = Math.min(b1.xMax, b2.xMax) - x; @@ -38169,57 +36945,37 @@ var DisplayObjectDefinition = function () { } }, _invalidateTransform: function () { - var stack = [ - this - ]; - while (stack.length) { - var node = stack.pop(); - if (node._concatenatedTransform && !node._concatenatedTransform.invalid) { - node._concatenatedTransform.invalid = true; - var children = node._children; - for (var i = 0; i < children.length; i++) { - stack.push(children[i]); - } - } - } + this._concatenatedTransform.invalid = true; }, - _updateCurrentTransform: function () { - var scaleX = this._scaleX; - var scaleY = this._scaleY; - var rotation, u, v; - switch (this._rotation) { - case 0: - case 360: - u = 1; - v = 0; - break; - case 90: - case -270: - u = 0; - v = 1; - break; - case 180: - case -180: - u = -1; - v = 0; - break; - case 270: - case -90: - u = 0; - v = -1; - break; - default: - rotation = this._rotation / 180 * Math.PI; - u = Math.cos(rotation); - v = Math.sin(rotation); - break; + _setTransformMatrix: function (matrix, convertToTwips) { + var a = matrix.a; + var b = matrix.b; + var c = matrix.c; + var d = matrix.d; + var tx, ty; + if (convertToTwips) { + tx = matrix.tx * 20 | 0; + ty = matrix.ty * 20 | 0; + } else { + tx = matrix.tx; + ty = matrix.ty; } - this._invalidateTransform(); + var angle = a !== 0 ? Math.atan(b / a) : b > 0 ? Math.PI / 2 : -Math.PI / 2; + this._rotation = angle * 180 / Math.PI; + this._rotationCos = Math.cos(angle); + this._rotationSin = Math.sin(angle); + var sx = Math.sqrt(a * a + b * b); + this._scaleX = a > 0 ? sx : -sx; + var sy = Math.sqrt(d * d + c * c); + this._scaleY = d > 0 ? sy : -sy; var transform = this._currentTransform; - transform.a = u * scaleX; - transform.b = v * scaleX; - transform.c = -v * scaleY; - transform.d = u * scaleY; + transform.a = a; + transform.b = b; + transform.c = c; + transform.d = d; + transform.tx = tx; + transform.ty = ty; + this._invalidateTransform(); }, get accessibilityProperties() { return this._accessibilityProperties; @@ -38234,8 +36990,9 @@ var DisplayObjectDefinition = function () { if (val === this._alpha) { return; } - this._alpha = val; this._invalidate(); + this._alpha = val; + this._animated = false; }, get blendMode() { return this._blendMode; @@ -38246,12 +37003,14 @@ var DisplayObjectDefinition = function () { } else { throwError('ArgumentError', Errors.InvalidEnumError, 'blendMode'); } + this._animated = false; }, get cacheAsBitmap() { return this._cacheAsBitmap; }, set cacheAsBitmap(val) { this._cacheAsBitmap = this._filters.length ? true : val; + this._animated = false; }, get filters() { return this._filters; @@ -38265,6 +37024,7 @@ var DisplayObjectDefinition = function () { this._cacheAsBitmap = this._wasCachedAsBitmap; } this._filters = val; + this._animated = false; }, get height() { var bounds = this._getContentBounds(); @@ -38275,12 +37035,11 @@ var DisplayObjectDefinition = function () { if (val < 0) { return; } - var rotation = this._rotation / 180 * Math.PI; - var u = Math.abs(Math.cos(rotation)); - var v = Math.abs(Math.sin(rotation)); + var u = Math.abs(this._rotationCos); + var v = Math.abs(this._rotationSin); var bounds = this._getContentBounds(); var baseHeight = v * (bounds.xMax - bounds.xMin) + u * (bounds.yMax - bounds.yMin); - if (baseHeight === 0) { + if (!baseHeight) { return; } var baseWidth = u * (bounds.xMax - bounds.xMin) + v * (bounds.yMax - bounds.yMin); @@ -38297,6 +37056,7 @@ var DisplayObjectDefinition = function () { if (this._mask === val) { return; } + this._invalidate(); if (val && val._maskedObject) { val._maskedObject.mask = null; } @@ -38304,7 +37064,7 @@ var DisplayObjectDefinition = function () { if (val) { val._maskedObject = this; } - this._invalidate(); + this._animated = false; }, get name() { return this._name || (this._name = generateName()); @@ -38313,22 +37073,29 @@ var DisplayObjectDefinition = function () { this._name = val; }, get mouseX() { - p1.x = this._stage._mouseX; - p1.y = this._stage._mouseY; - this._applyCurrentInverseTransform(p1); - return p1.x; + if (!this._stage) { + return 0; + } + point.x = this._stage._mouseX; + point.y = this._stage._mouseY; + this._applyConcatenatedInverseTransform(point); + return point.x; }, get mouseY() { - p1.x = this._stage._mouseX; - p1.y = this._stage._mouseY; - this._applyCurrentInverseTransform(p1); - return p1.y; + if (!this._stage) { + return 0; + } + point.x = this._stage._mouseX; + point.y = this._stage._mouseY; + this._applyConcatenatedInverseTransform(point); + return point.y; }, get opaqueBackground() { return this._opaqueBackground; }, set opaqueBackground(val) { this._opaqueBackground = val; + this._animated = false; }, get parent() { return this._index > -1 ? this._parent : null; @@ -38348,8 +37115,40 @@ var DisplayObjectDefinition = function () { return; this._invalidate(); this._invalidateBounds(); + this._invalidateTransform(); this._rotation = val; - this._updateCurrentTransform(); + var u, v; + switch (val) { + case 0: + case 360: + u = 1, v = 0; + break; + case 90: + case -270: + u = 0, v = 1; + break; + case 180: + case -180: + u = -1, v = 0; + break; + case 270: + case -90: + u = 0, v = -1; + break; + default: + var angle = this._rotation / 180 * Math.PI; + u = Math.cos(angle); + v = Math.sin(angle); + break; + } + this._rotationCos = u; + this._rotationSin = v; + var m = this._currentTransform; + m.a = u * this._scaleX; + m.b = v * this._scaleX; + m.c = -v * this._scaleY; + m.d = u * this._scaleY; + this._animated = false; }, get rotationX() { return 0; @@ -38381,8 +37180,12 @@ var DisplayObjectDefinition = function () { return; this._invalidate(); this._invalidateBounds(); + this._invalidateTransform(); this._scaleX = val; - this._updateCurrentTransform(); + var m = this._currentTransform; + m.a = this._rotationCos * val; + m.b = this._rotationSin * val; + this._animated = false; }, get scaleY() { return this._scaleY; @@ -38392,8 +37195,12 @@ var DisplayObjectDefinition = function () { return; this._invalidate(); this._invalidateBounds(); + this._invalidateTransform(); this._scaleY = val; - this._updateCurrentTransform(); + var m = this._currentTransform; + m.c = -this._rotationSin * val; + m.d = this._rotationCos * val; + this._animated = false; }, get scaleZ() { return 1; @@ -38407,6 +37214,7 @@ var DisplayObjectDefinition = function () { set scale9Grid(val) { somewhatImplemented('DisplayObject.scale9Grid'); this._scale9Grid = val; + this._animated = false; }, get scrollRect() { return this._scrollRect; @@ -38416,12 +37224,11 @@ var DisplayObjectDefinition = function () { this._scrollRect = val; }, get transform() { - return this._transform || new flash.geom.Transform(this); + return new flash.geom.Transform(this); }, set transform(val) { - this._animated = false; this._invalidateBounds(); - var transform = this._transform; + var transform = this.transform; transform.colorTransform = val.colorTransform; if (val.matrix3D) { transform.matrix3D = val.matrix3D; @@ -38435,8 +37242,9 @@ var DisplayObjectDefinition = function () { set visible(val) { if (val === this._visible) return; - this._visible = val; this._invalidate(); + this._visible = val; + this._animated = false; }, get width() { var bounds = this._getContentBounds(); @@ -38447,12 +37255,11 @@ var DisplayObjectDefinition = function () { if (val < 0) { return; } - var rotation = this._rotation / 180 * Math.PI; - var u = Math.abs(Math.cos(rotation)); - var v = Math.abs(Math.sin(rotation)); + var u = Math.abs(this._rotationCos); + var v = Math.abs(this._rotationSin); var bounds = this._getContentBounds(); var baseWidth = u * (bounds.xMax - bounds.xMin) + v * (bounds.yMax - bounds.yMin); - if (baseWidth === 0) { + if (!baseWidth) { return; } var baseHeight = v * (bounds.xMax - bounds.xMin) + u * (bounds.yMax - bounds.yMin); @@ -38470,6 +37277,7 @@ var DisplayObjectDefinition = function () { this._invalidateBounds(); this._invalidateTransform(); this._currentTransform.tx = val; + this._animated = false; }, get y() { return this._currentTransform.ty; @@ -38482,6 +37290,7 @@ var DisplayObjectDefinition = function () { this._invalidateBounds(); this._invalidateTransform(); this._currentTransform.ty = val; + this._animated = false; }, get z() { return 0; @@ -38507,10 +37316,7 @@ var DisplayObjectDefinition = function () { var numChildren = children.length; for (var i = 0; i < numChildren; i++) { var child = children[i]; - if (!child._visible) { - continue; - } - var b = child.getBounds(null); + var b = child.getBounds(this); var x1 = b.xMin; var y1 = b.yMin; var x2 = b.xMax; @@ -38553,6 +37359,9 @@ var DisplayObjectDefinition = function () { return this._getTransformedRect(this._getContentBounds(), targetCoordSpace); }, _getTransformedRect: function (rect, targetCoordSpace) { + if (!targetCoordSpace || targetCoordSpace === this) { + return rect; + } if (rect.xMax - rect.xMin === 0 || rect.yMax - rect.yMin === 0) { return { xMin: 0, @@ -38561,19 +37370,19 @@ var DisplayObjectDefinition = function () { yMax: 0 }; } - p1.x = rect.xMin; - p1.y = rect.yMin; - p2.x = rect.xMax; - p2.y = rect.yMin; - p3.x = rect.xMax; - p3.y = rect.yMax; - p4.x = rect.xMin; - p4.y = rect.yMax; - this._applyCurrentTransform(targetCoordSpace, p1, p2, p3, p4); - var xMin = Math.min(p1.x, p2.x, p3.x, p4.x); - var xMax = Math.max(p1.x, p2.x, p3.x, p4.x); - var yMin = Math.min(p1.y, p2.y, p3.y, p4.y); - var yMax = Math.max(p1.y, p2.y, p3.y, p4.y); + topLeft.x = rect.xMin; + topLeft.y = rect.yMin; + topRight.x = rect.xMax; + topRight.y = rect.yMin; + bottomRight.x = rect.xMax; + bottomRight.y = rect.yMax; + bottomLeft.x = rect.xMin; + bottomLeft.y = rect.yMax; + this._applyCurrentTransform(targetCoordSpace, topLeft, topRight, bottomRight, bottomLeft); + var xMin = Math.min(topLeft.x, topRight.x, bottomRight.x, bottomLeft.x); + var xMax = Math.max(topLeft.x, topRight.x, bottomRight.x, bottomLeft.x); + var yMin = Math.min(topLeft.y, topRight.y, bottomRight.y, bottomLeft.y); + var yMax = Math.max(topLeft.y, topRight.y, bottomRight.y, bottomLeft.y); return { xMin: xMin, yMin: yMin, @@ -38690,20 +37499,16 @@ var DisplayObjectDefinition = function () { loaderInfo: desc(def, 'loaderInfo'), accessibilityProperties: desc(def, 'accessibilityProperties'), globalToLocal: function (pt) { - var twipPt = { - x: pt.x * 20 | 0, - y: pt.y * 20 | 0 - }; - this._applyCurrentInverseTransform(twipPt); - return new flash.geom.Point(twipPt.x / 20, twipPt.y / 20); + point.x = pt.x * 20 | 0; + point.y = pt.y * 20 | 0; + this._applyConcatenatedInverseTransform(point); + return new flash.geom.Point(point.x / 20, point.y / 20); }, localToGlobal: function (pt) { - var twipPt = { - x: pt.x * 20 | 0, - y: pt.y * 20 | 0 - }; - this._applyCurrentTransform(this._stage, twipPt); - return new flash.geom.Point(twipPt.x / 20, twipPt.y / 20); + point.x = pt.x * 20 | 0; + point.y = pt.y * 20 | 0; + this._applyCurrentTransform(this._stage, point); + return new flash.geom.Point(point.x / 20, point.y / 20); }, getBounds: function (targetCoordSpace) { var bounds = this.getBounds(targetCoordSpace); @@ -38773,7 +37578,7 @@ var DisplayObjectContainerDefinition = function () { child._index = index; child._invalidateTransform(); this._invalidateBounds(); - child._dispatchEvent('added'); + child._dispatchEvent('added', undefined, true); if (this._stage) { this._stage._addToStage(child); } @@ -38827,7 +37632,7 @@ var DisplayObjectContainerDefinition = function () { throwError('RangeError', Errors.ParamRangeError); } var child = children[index]; - child._dispatchEvent('removed'); + child._dispatchEvent('removed', undefined, true); if (this._stage) { this._stage._removeFromStage(child); } @@ -38868,14 +37673,22 @@ var DisplayObjectContainerDefinition = function () { child._invalidate(); return child; }, - removeChildren: function (begin, end) { - var children = this._children; - var numChildren = children.length; - if (begin < 0 || begin > numChildren || end < 0 || end < begin || end > numChildren) { + removeChildren: function (beginIndex, endIndex) { + beginIndex = arguments.length < 1 ? 0 : beginIndex | 0; + endIndex = arguments.length < 2 ? 2147483647 : endIndex | 0; + var numChildren = this._children.length; + if (beginIndex < 0 || endIndex < 0 || endIndex < beginIndex) { throwError('RangeError', Errors.ParamRangeError); } - for (var i = begin; i < end; i++) { - this.removeChildAt(i); + if (numChildren === 0) { + return; + } + if (endIndex > numChildren - 1) { + endIndex = numChildren - 1; + } + var count = endIndex - beginIndex + 1; + while (count--) { + this.removeChildAt(beginIndex); } }, swapChildren: function (child1, child2) { @@ -38919,6 +37732,7 @@ var DisplayObjectContainerDefinition = function () { this._mouseChildren = true; this._tabChildren = true; this._sparse = false; + this._isContainer = true; }; def.__glue__ = { native: { @@ -38982,8 +37796,12 @@ var GraphicsDefinition = function () { this.beginPath(); this._bitmap = null; this._parent = 0; + this.bbox = null; + this.strokeBbox = null; }, _invalidate: function () { + this.bbox = null; + this.strokeBbox = null; if (this._parent._stage) { this._parent._stage._invalidateOnStage(this._parent); } @@ -39205,12 +38023,13 @@ var GraphicsDefinition = function () { if (this.bbox) { return this.bbox; } + Counter.count('CACHE ME: Graphics._getBounds'); var subpaths = this._paths; var xMins = [], yMins = [], xMaxs = [], yMaxs = []; for (var i = 0, n = subpaths.length; i < n; i++) { var path = subpaths[i]; if (path.commands.length) { - var b = path.getBounds(true); + var b = path.getBounds(includeStroke); if (b) { xMins.push(b.xMin); yMins.push(b.yMin); @@ -39222,16 +38041,18 @@ var GraphicsDefinition = function () { if (xMins.length === 0) { return 0; } - var xMin = Math.min.apply(Math, xMins); - var yMin = Math.min.apply(Math, yMins); - var xMax = Math.max.apply(Math, xMaxs); - var yMax = Math.max.apply(Math, yMaxs); - return { - xMin: xMin, - yMin: yMin, - xMax: xMax, - yMax: yMax + bbox = { + xMin: Math.min.apply(Math, xMins), + yMin: Math.min.apply(Math, yMins), + xMax: Math.max.apply(Math, xMaxs), + yMax: Math.max.apply(Math, yMaxs) }; + if (includeStroke) { + this.strokeBbox = bbox; + } else { + this.bbox = bbox; + } + return bbox; } }; def.__glue__ = { @@ -39425,424 +38246,7 @@ var InteractiveObjectDefinition = function () { var $RELEASE = false; var LoaderDefinition = function () { var WORKERS_ENABLED = true; - var LOADER_PATH; - var workerScripts; - if (true) { - LOADER_PATH = 'shumway-worker.js'; - } else { - LOADER_PATH = 'flash/display/Loader.js'; - workerScripts = [ - '../../../lib/DataView.js/DataView.js', - '../util.js', - '../../swf/config.js', - '../../swf/util.js', - '../../swf/swf.js', - '../../swf/types.js', - '../../swf/structs.js', - '../../swf/tags.js', - '../../swf/inflate.js', - '../../swf/stream.js', - '../../swf/templates.js', - '../../swf/generator.js', - '../../swf/handlers.js', - '../../swf/parser.js', - '../../swf/bitmap.js', - '../../swf/button.js', - '../../swf/font.js', - '../../swf/image.js', - '../../swf/label.js', - '../../swf/shape.js', - '../../swf/sound.js', - '../../swf/text.js' - ]; - } - var isWorker = typeof window === 'undefined'; - function loadFromWorker(loader, request, context) { - var symbols = {}; - var commitData; - if (loader) { - commitData = function (data) { - return loader._commitData(data); - }; - } else { - commitData = function (data) { - self.postMessage(data); - }; - } - function defineSymbol(swfTag) { - var symbol; - switch (swfTag.code) { - case SWF_TAG_CODE_DEFINE_BITS: - case SWF_TAG_CODE_DEFINE_BITS_JPEG2: - case SWF_TAG_CODE_DEFINE_BITS_JPEG3: - case SWF_TAG_CODE_DEFINE_BITS_JPEG4: - case SWF_TAG_CODE_JPEG_TABLES: - symbol = defineImage(swfTag, symbols); - break; - case SWF_TAG_CODE_DEFINE_BITS_LOSSLESS: - case SWF_TAG_CODE_DEFINE_BITS_LOSSLESS2: - symbol = defineBitmap(swfTag, symbols); - break; - case SWF_TAG_CODE_DEFINE_BUTTON: - case SWF_TAG_CODE_DEFINE_BUTTON2: - symbol = defineButton(swfTag, symbols); - break; - case SWF_TAG_CODE_DEFINE_EDIT_TEXT: - symbol = defineText(swfTag, symbols); - break; - case SWF_TAG_CODE_DEFINE_FONT: - case SWF_TAG_CODE_DEFINE_FONT2: - case SWF_TAG_CODE_DEFINE_FONT3: - case SWF_TAG_CODE_DEFINE_FONT4: - symbol = defineFont(swfTag, symbols); - break; - case SWF_TAG_CODE_DEFINE_MORPH_SHAPE: - case SWF_TAG_CODE_DEFINE_MORPH_SHAPE2: - case SWF_TAG_CODE_DEFINE_SHAPE: - case SWF_TAG_CODE_DEFINE_SHAPE2: - case SWF_TAG_CODE_DEFINE_SHAPE3: - case SWF_TAG_CODE_DEFINE_SHAPE4: - symbol = defineShape(swfTag, symbols); - break; - case SWF_TAG_CODE_DEFINE_SOUND: - symbol = defineSound(swfTag, symbols); - break; - case SWF_TAG_CODE_DEFINE_BINARY_DATA: - symbol = { - type: 'binary', - id: swfTag.id, - data: swfTag.data - }; - break; - case SWF_TAG_CODE_DEFINE_SPRITE: - var depths = {}; - var frame = { - type: 'frame' - }; - var frames = []; - var tags = swfTag.tags; - var frameScripts = null; - var frameIndex = 0; - var soundStream = null; - for (var i = 0, n = tags.length; i < n; i++) { - var tag = tags[i]; - switch (tag.code) { - case SWF_TAG_CODE_DO_ACTION: - if (!frameScripts) - frameScripts = []; - frameScripts.push(frameIndex); - frameScripts.push(tag.actionsData); - break; - case SWF_TAG_CODE_START_SOUND: - var startSounds = frame.startSounds || (frame.startSounds = []); - startSounds.push(tag); - break; - case SWF_TAG_CODE_SOUND_STREAM_HEAD: - try { - soundStream = createSoundStream(tag); - frame.soundStream = soundStream.info; - } catch (e) { - } - break; - case SWF_TAG_CODE_SOUND_STREAM_BLOCK: - if (soundStream) { - frame.soundStreamBlock = soundStream.decode(tag.data); - } - break; - case SWF_TAG_CODE_FRAME_LABEL: - frame.labelName = tag.name; - break; - case SWF_TAG_CODE_PLACE_OBJECT: - case SWF_TAG_CODE_PLACE_OBJECT2: - case SWF_TAG_CODE_PLACE_OBJECT3: - depths[tag.depth] = tag; - break; - case SWF_TAG_CODE_REMOVE_OBJECT: - case SWF_TAG_CODE_REMOVE_OBJECT2: - depths[tag.depth] = null; - break; - case SWF_TAG_CODE_SHOW_FRAME: - var repeat = 1; - while (i < n - 1) { - var nextTag = tags[i + 1]; - if (nextTag.code !== SWF_TAG_CODE_SHOW_FRAME) - break; - i++; - repeat++; - } - frameIndex += repeat; - frame.repeat = repeat; - frame.depths = depths; - frames.push(frame); - depths = {}; - frame = { - type: 'frame' - }; - break; - } - } - symbol = { - type: 'sprite', - id: swfTag.id, - frameCount: swfTag.frameCount, - frames: frames, - frameScripts: frameScripts - }; - break; - case SWF_TAG_CODE_DEFINE_TEXT: - case SWF_TAG_CODE_DEFINE_TEXT2: - symbol = defineLabel(swfTag, symbols); - break; - } - if (!symbol) { - commitData({ - command: 'error', - message: 'unknown symbol type: ' + swfTag.code - }); - return; - } - symbol.isSymbol = true; - symbols[swfTag.id] = symbol; - commitData(symbol); - } - function createParsingContext() { - var depths = {}; - var frame = { - type: 'frame' - }; - var tagsProcessed = 0; - var soundStream = null; - var lastProgressSent = 0; - return { - onstart: function (result) { - commitData({ - command: 'init', - result: result - }); - }, - onprogress: function (result) { - if (Date.now() - lastProgressSent > 1000 / 24 || result.bytesLoaded === result.bytesTotal) { - commitData({ - command: 'progress', - result: { - bytesLoaded: result.bytesLoaded, - bytesTotal: result.bytesTotal - } - }); - lastProgressSent = Date.now(); - } - var tags = result.tags; - for (var n = tags.length; tagsProcessed < n; tagsProcessed++) { - var tag = tags[tagsProcessed]; - if ('id' in tag) { - defineSymbol(tag); - continue; - } - switch (tag.code) { - case SWF_TAG_CODE_DEFINE_SCENE_AND_FRAME_LABEL_DATA: - frame.sceneData = tag; - break; - case SWF_TAG_CODE_DEFINE_SCALING_GRID: - var symbolUpdate = { - isSymbol: true, - id: tag.symbolId, - updates: { - scale9Grid: tag.splitter - } - }; - commitData(symbolUpdate); - break; - case SWF_TAG_CODE_DO_ABC: - case SWF_TAG_CODE_DO_ABC_: - var abcBlocks = frame.abcBlocks; - if (abcBlocks) - abcBlocks.push({ - data: tag.data, - flags: tag.flags - }); - else - frame.abcBlocks = [ - { - data: tag.data, - flags: tag.flags - } - ]; - break; - case SWF_TAG_CODE_DO_ACTION: - var actionBlocks = frame.actionBlocks; - if (actionBlocks) - actionBlocks.push(tag.actionsData); - else - frame.actionBlocks = [ - tag.actionsData - ]; - break; - case SWF_TAG_CODE_DO_INIT_ACTION: - var initActionBlocks = frame.initActionBlocks || (frame.initActionBlocks = []); - initActionBlocks.push({ - spriteId: tag.spriteId, - actionsData: tag.actionsData - }); - break; - case SWF_TAG_CODE_START_SOUND: - var startSounds = frame.startSounds; - if (!startSounds) - frame.startSounds = startSounds = []; - startSounds.push(tag); - break; - case SWF_TAG_CODE_SOUND_STREAM_HEAD: - try { - soundStream = createSoundStream(tag); - frame.soundStream = soundStream.info; - } catch (e) { - } - break; - case SWF_TAG_CODE_SOUND_STREAM_BLOCK: - if (soundStream) { - frame.soundStreamBlock = soundStream.decode(tag.data); - } - break; - case SWF_TAG_CODE_SYMBOL_CLASS: - var exports = frame.exports; - if (exports) - frame.exports = exports.concat(tag.exports); - else - frame.exports = tag.exports.slice(0); - break; - case SWF_TAG_CODE_FRAME_LABEL: - frame.labelName = tag.name; - break; - case SWF_TAG_CODE_PLACE_OBJECT: - case SWF_TAG_CODE_PLACE_OBJECT2: - case SWF_TAG_CODE_PLACE_OBJECT3: - depths[tag.depth] = tag; - break; - case SWF_TAG_CODE_REMOVE_OBJECT: - case SWF_TAG_CODE_REMOVE_OBJECT2: - depths[tag.depth] = null; - break; - case SWF_TAG_CODE_SET_BACKGROUND_COLOR: - frame.bgcolor = tag.color; - break; - case SWF_TAG_CODE_SHOW_FRAME: - var repeat = 1; - while (tagsProcessed < n) { - var nextTag = tags[tagsProcessed + 1]; - if (!nextTag || nextTag.code !== SWF_TAG_CODE_SHOW_FRAME) - break; - tagsProcessed++; - repeat++; - } - frame.repeat = repeat; - frame.depths = depths; - commitData(frame); - depths = {}; - frame = { - type: 'frame' - }; - break; - } - } - }, - oncomplete: function (result) { - commitData(result); - var stats; - if (typeof result.swfVersion === 'number') { - var bbox = result.bbox; - stats = { - topic: 'parseInfo', - parseTime: result.parseTime, - bytesTotal: result.bytesTotal, - swfVersion: result.swfVersion, - frameRate: result.frameRate, - width: (bbox.xMax - bbox.xMin) / 20, - height: (bbox.yMax - bbox.yMin) / 20, - isAvm2: !(!result.fileAttributes.doAbc) - }; - } - commitData({ - command: 'complete', - stats: stats - }); - } - }; - } - function parseBytes(bytes) { - SWF.parse(bytes, createParsingContext()); - } - if (isWorker || !flash.net.URLRequest.class.isInstanceOf(request)) { - var input = request; - if (input instanceof ArrayBuffer) { - parseBytes(input); - } else if ('subscribe' in input) { - var pipe = SWF.parseAsync(createParsingContext()); - input.subscribe(function (data, progress) { - if (data) { - pipe.push(data, progress); - } else { - pipe.close(); - } - }); - } else if (typeof FileReaderSync !== 'undefined') { - var reader = new FileReaderSync(); - var buffer = reader.readAsArrayBuffer(input); - parseBytes(buffer); - } else { - var reader = new FileReader(); - reader.onload = function () { - parseBytes(this.result); - }; - reader.readAsArrayBuffer(input); - } - } else { - var session = FileLoadingService.createSession(); - var pipe = SWF.parseAsync(createParsingContext()); - session.onprogress = function (data, progressState) { - pipe.push(data, progressState); - var data = { - command: 'progress', - result: { - bytesLoaded: progressState.bytesLoaded, - bytesTotal: progressState.bytesTotal - } - }; - loader._commitData(data); - }; - session.onerror = function (error) { - loader._commitData({ - command: 'error', - error: error - }); - }; - session.onopen = function () { - }; - session.onclose = function () { - pipe.close(); - }; - session.open(request._toFileRequest()); - } - } - if (isWorker) { - if (workerScripts) { - importScripts.apply(null, workerScripts); - } - var subscription = null; - self.onmessage = function (evt) { - if (subscription) { - subscription.callback(evt.data.data, evt.data.progress); - } else if (evt.data === 'pipe:') { - subscription = { - subscribe: function (callback) { - this.callback = callback; - } - }; - loadFromWorker(null, subscription); - } else { - loadFromWorker(null, evt.data); - } - }; - return; - } + var LOADER_PATH = true ? 'shumway-worker.js' : 'swf/resourceloader.js'; var head = document.head; head.insertBefore(document.createElement('style'), head.firstChild); var style = document.styleSheets[0]; @@ -39875,7 +38279,8 @@ var LoaderDefinition = function () { } }); Promise.when(frameConstructed, this._lastPromise).then(function () { - this.contentLoaderInfo._dispatchEvent('complete'); + this._content._complete = true; + this._contentLoaderInfo._dispatchEvent('complete'); }.bind(this)); var stats = data.stats; if (stats) { @@ -39888,7 +38293,7 @@ var LoaderDefinition = function () { this._lastPromise.resolve(); break; case 'error': - this.contentLoaderInfo._dispatchEvent('ioError', flash.events.IOErrorEvent); + this._contentLoaderInfo._dispatchEvent('ioError', flash.events.IOErrorEvent); break; default: if (data.id === 0) @@ -39903,7 +38308,7 @@ var LoaderDefinition = function () { } }, _updateProgress: function (state) { - var loaderInfo = this.contentLoaderInfo; + var loaderInfo = this._contentLoaderInfo; loaderInfo._bytesLoaded = state.bytesLoaded || 0; loaderInfo._bytesTotal = state.bytesTotal || 0; var event = new flash.events.ProgressEvent('progress', false, false, loaderInfo._bytesLoaded, loaderInfo._bytesTotal); @@ -39967,10 +38372,11 @@ var LoaderDefinition = function () { var actionBlocks = frame.actionBlocks; var initActionBlocks = frame.initActionBlocks; var exports = frame.exports; + var symbolClasses = frame.symbolClasses; var sceneData = frame.sceneData; var loader = this; var dictionary = loader._dictionary; - var loaderInfo = loader.contentLoaderInfo; + var loaderInfo = loader._contentLoaderInfo; var timeline = loader._timeline; var frameNum = timeline.length + 1; var framePromise = new Promise(); @@ -40002,7 +38408,25 @@ var LoaderDefinition = function () { } } } - if (exports && loader._isAvm2Enabled) { + if (symbolClasses && loader._isAvm2Enabled) { + var symbolClassesPromises = []; + for (var i = 0, n = symbolClasses.length; i < n; i++) { + var asset = symbolClasses[i]; + var symbolPromise = dictionary[asset.symbolId]; + if (!symbolPromise) + continue; + symbolPromise.then(function (symbolPromise, className) { + return function symbolPromiseResolved() { + var symbolInfo = symbolPromise.value; + symbolInfo.className = className; + avm2.applicationDomain.getClass(className).setSymbol(symbolInfo.props); + }; + }(symbolPromise, asset.className)); + symbolClassesPromises.push(symbolPromise); + } + return Promise.when.apply(Promise, symbolClassesPromises); + } + if (exports && !loader._isAvm2Enabled) { var exportPromises = []; for (var i = 0, n = exports.length; i < n; i++) { var asset = exports[i]; @@ -40012,8 +38436,7 @@ var LoaderDefinition = function () { symbolPromise.then(function (symbolPromise, className) { return function symbolPromiseResolved() { var symbolInfo = symbolPromise.value; - symbolInfo.className = className; - avm2.applicationDomain.getClass(className).setSymbol(symbolInfo.props); + loader._avm1Context.addAsset(className, symbolInfo.props); }; }(symbolPromise, asset.className)); exportPromises.push(symbolPromise); @@ -40036,7 +38459,8 @@ var LoaderDefinition = function () { level: parent ? 0 : -1, timeline: timeline, totalFrames: rootInfo.props.totalFrames, - stage: loader._stage + stage: loader._stage, + complete: frame.complete }); var isRootMovie = parent && parent == loader._stage && loader._stage._children.length === 0; if (isRootMovie) { @@ -40083,6 +38507,7 @@ var LoaderDefinition = function () { labels.push(new flash.display.FrameLabel(labelName, frameNum)); } var scene = new flash.display.Scene('Scene 1', labels, root.symbol.totalFrames); + scene._startFrame = 1; scene._endFrame = root.symbol.totalFrames; root.symbol.scenes = [ scene @@ -40092,9 +38517,14 @@ var LoaderDefinition = function () { var avm1Context = loader._avm1Context; var _root = root; if (parent && parent !== loader._stage) { - var parentLoader = parent._loader; + var parentLoader = parent.loaderInfo._loader; while (parentLoader._parent && parentLoader._parent !== loader._stage) { - parentLoader = parentLoader._parent._loader; + parentLoader = parentLoader._parent.loaderInfo._loader; + } + if (parentLoader._isAvm2Enabled) { + somewhatImplemented('AVM1Movie'); + this._worker && this._worker.terminate(); + return; } _root = parentLoader._content; } @@ -40109,10 +38539,13 @@ var LoaderDefinition = function () { } } } + if (loader._stage) { + loader._stage._children[0] = root; + } rootClass.instanceConstructor.call(root); loader._content = root; } else { - root._framesLoaded += frame.repeat; + root._framesLoaded = timeline.length; if (labelName && root._labelMap) { if (root._labelMap[labelName] === undefined) { root._labelMap[labelName] = frameNum; @@ -40145,7 +38578,7 @@ var LoaderDefinition = function () { if (state.executed) return; state.executed = true; - return executeActions(actionsData, avm1Context, this._getAS2Object(), exports); + return executeActions(actionsData, avm1Context, this._getAS2Object()); }.bind(root, actionsData, spriteId, { executed: false })); @@ -40156,7 +38589,7 @@ var LoaderDefinition = function () { var block = actionBlocks[i]; root.addFrameScript(frameNum - 1, function (block) { return function () { - return executeActions(block, avm1Context, this._getAS2Object(), exports); + return executeActions(block, avm1Context, this._getAS2Object()); }; }(block)); } @@ -40187,7 +38620,7 @@ var LoaderDefinition = function () { image._parent = loader; loader._content = image; imgPromise.resolve(imageInfo); - loader.contentLoaderInfo._dispatchEvent('init'); + loader._contentLoaderInfo._dispatchEvent('init'); }; img.src = URL.createObjectURL(imageInfo.data); delete imageInfo.data; @@ -40429,7 +38862,7 @@ var LoaderDefinition = function () { }, _init: function (info) { var loader = this; - var loaderInfo = loader.contentLoaderInfo; + var loaderInfo = loader._contentLoaderInfo; loaderInfo._swfVersion = info.swfVersion; var bbox = info.bbox; loaderInfo._width = bbox.xMax - bbox.xMin; @@ -40452,43 +38885,45 @@ var LoaderDefinition = function () { this._setup(); }, _load: function (request, checkPolicyFile, applicationDomain, securityDomain, deblockingFilter) { - if (!isWorker && flash.net.URLRequest.class.isInstanceOf(request)) { + if (flash.net.URLRequest.class.isInstanceOf(request)) { this._contentLoaderInfo._url = request._url; } - if (!isWorker && WORKERS_ENABLED) { - var loader = this; - var worker = loader._worker = new Worker(SHUMWAY_ROOT + LOADER_PATH); - worker.onmessage = function (evt) { - loader._commitData(evt.data); - }; - if (flash.net.URLRequest.class.isInstanceOf(request)) { - var session = FileLoadingService.createSession(); - session.onprogress = function (data, progress) { - worker.postMessage({ - data: data, - progress: progress - }); - }; - session.onerror = function (error) { - loader._commitData({ - command: 'error', - error: error - }); - }; - session.onopen = function () { - worker.postMessage('pipe:'); - }; - session.onclose = function () { - worker.postMessage({ - data: null - }); - }; - session.open(request._toFileRequest()); - } else { - worker.postMessage(request); - } + var worker; + if (WORKERS_ENABLED) { + worker = new Worker(SHUMWAY_ROOT + LOADER_PATH); } else { - loadFromWorker(this, request); + worker = new ResourceLoader(window); + } + var loader = this; + loader._worker = worker; + worker.onmessage = function (evt) { + loader._commitData(evt.data); + }; + if (flash.net.URLRequest.class.isInstanceOf(request)) { + var session = FileLoadingService.createSession(); + session.onprogress = function (data, progress) { + worker.postMessage({ + data: data, + progress: progress + }); + }; + session.onerror = function (error) { + loader._commitData({ + command: 'error', + error: error + }); + }; + session.onopen = function () { + worker.postMessage('pipe:'); + }; + session.onclose = function () { + worker.postMessage({ + data: null + }); + }; + session.open(request._toFileRequest()); + } else { + worker.postMessage(request); } }, _setup: function () { @@ -40505,7 +38940,7 @@ var LoaderDefinition = function () { return; } var loaded = function () { - var loaderInfo = loader.contentLoaderInfo; + var loaderInfo = loader._contentLoaderInfo; var avm1Context = new AS2Context(loaderInfo._swfVersion); avm1Context.stage = stage; loader._avm1Context = avm1Context; @@ -40539,7 +38974,7 @@ var LoaderDefinition = function () { }, _load: def._load, _loadBytes: function _loadBytes(bytes, checkPolicyFile, applicationDomain, securityDomain, requestedContentParent, parameters, deblockingFilter, allowLoadBytesCodeExecution, imageDecodingPolicy) { - def._load(bytes.a); + def._load.call(this, bytes.a, checkPolicyFile, applicationDomain, securityDomain); }, _unload: function _unload(halt, gc) { somewhatImplemented('Loader._unload, do we even need to do anything here?'); @@ -40780,6 +39215,7 @@ var MovieClipDefinition = function () { this._totalFrames = 1; this._startSoundRegistrations = []; this._allowFrameNavigation = true; + this._complete = true; var s = this.symbol; if (s) { this._timeline = s.timeline || null; @@ -40789,6 +39225,7 @@ var MovieClipDefinition = function () { this._totalFrames = s.totalFrames || 1; this._startSoundRegistrations = s.startSoundRegistrations || []; this._scenes = s.scenes || null; + this._complete = s.complete === false ? false : true; var map = this._labelMap; for (var name in map) { var frame = map[name]; @@ -40810,18 +39247,17 @@ var MovieClipDefinition = function () { self._postConstructChildren(); }; this._addEventListener('executeFrame', this._onExecuteFrame); - if (this._totalFrames <= 1) { + if (this._complete && this._totalFrames <= 1) { return this; } this._onAdvanceFrame = function onAdvanceFrame() { var frameNum = self._playHead + 1; - if (frameNum > self._totalFrames) { + if (self._complete && frameNum > self._totalFrames) { frameNum = 1; } else if (frameNum > self._framesLoaded) { return; } - self._destructChildren(frameNum); - self._declareChildren(frameNum); + self._updateDisplayList(frameNum); if (self._sparse) { self._addEventListener('constructChildren', self._onConstructChildren); } @@ -40837,6 +39273,10 @@ var MovieClipDefinition = function () { }; this.play(); }, + _updateDisplayList: function (nextFrameNum) { + this._destructChildren(nextFrameNum); + this._declareChildren(nextFrameNum); + }, _declareChildren: function declareChildren(nextFrameNum) { var currentFrame = this._currentFrame; if (nextFrameNum === currentFrame) { @@ -40871,41 +39311,27 @@ var MovieClipDefinition = function () { if (!nextCmd || nextCmd === currentCmd) { continue; } - if (currentCmd && currentChild && currentChild._animated && nextCmd.symbolId === currentCmd.symbolId && nextCmd.ratio === currentCmd.ratio) { - currentChild._invalidate(); - currentChild._invalidateBounds(); - if (nextCmd.hasMatrix) { - var m = nextCmd.matrix; - var a = m.a; - var b = m.b; - var c = m.c; - var d = m.d; - currentChild._rotation = Math.atan2(b, a) * 180 / Math.PI; - var sx = Math.sqrt(a * a + b * b); - currentChild._scaleX = a > 0 ? sx : -sx; - var sy = Math.sqrt(d * d + c * c); - currentChild._scaleY = d > 0 ? sy : -sy; - var t = currentChild._currentTransform; - t.a = a; - t.b = b; - t.c = c; - t.d = d; - t.tx = m.tx; - t.ty = m.ty; - currentChild._invalidateTransform(); - } - if (nextCmd.hasCxform) { - currentChild._cxform = nextCmd.cxform; - } - if (nextCmd.clip) { - currentChild._clipDepth = nextCmd.clipDepth; - } - if (nextCmd.hasName) { - currentChild.name = nextCmd.name; - } - if (nextCmd.blend) { - currentChild.blendMode = this._resolveBlendMode(nextCmd.blendMode); + if (currentCmd && currentChild && nextCmd.symbolId === currentCmd.symbolId && nextCmd.ratio === currentCmd.ratio) { + if (currentChild._animated) { + currentChild._invalidate(); + currentChild._invalidateBounds(); + if (nextCmd.hasMatrix) { + currentChild._setTransformMatrix(nextCmd.matrix, false); + } + if (nextCmd.hasCxform) { + currentChild._cxform = nextCmd.cxform; + } + if (nextCmd.clip) { + currentChild._clipDepth = nextCmd.clipDepth; + } + if (nextCmd.hasName) { + currentChild.name = nextCmd.name; + } + if (nextCmd.blend) { + currentChild.blendMode = this._resolveBlendMode(nextCmd.blendMode); + } } + currentDisplayListItem.cmd = nextCmd; continue; } var newDisplayListItem = this._addTimelineChild(nextCmd, index); @@ -40928,31 +39354,31 @@ var MovieClipDefinition = function () { if (nextDisplayList === timeline[currentFrame - 1]) { return; } - var prevDisplayListItem = null; - var currentDisplayListItem = this._currentDisplayList; + var prevEntry = null; + var currentEntry = this._currentDisplayList; var toRemove = null; - while (currentDisplayListItem) { - var depth = currentDisplayListItem.depth; - var currentCmd = currentDisplayListItem.cmd; + while (currentEntry) { + var depth = currentEntry.depth; + var currentCmd = currentEntry.cmd; var nextCmd = nextDisplayList[depth]; if (!nextCmd || nextCmd.symbolId !== currentCmd.symbolId || nextCmd.ratio !== currentCmd.ratio) { - var nextDisplayListItem = currentDisplayListItem.next; - if (prevDisplayListItem) { - prevDisplayListItem.next = nextDisplayListItem; + var nextDisplayListItem = currentEntry.next; + if (prevEntry) { + prevEntry.next = nextDisplayListItem; } else { this._currentDisplayList = nextDisplayListItem; } - currentDisplayListItem.next = toRemove; - toRemove = currentDisplayListItem; - currentDisplayListItem = nextDisplayListItem; + currentEntry.next = toRemove; + toRemove = currentEntry; + currentEntry = nextDisplayListItem; } else { - prevDisplayListItem = currentDisplayListItem; - currentDisplayListItem = currentDisplayListItem.next; + prevEntry = currentEntry; + currentEntry = currentEntry.next; } } while (toRemove) { var child = toRemove.obj; - if (child) { + if (child && child._owned) { this._sparse = true; this.removeChild(child); child.destroy(); @@ -40967,8 +39393,7 @@ var MovieClipDefinition = function () { var enterFrame = frameNum !== this._currentFrame; if (this._allowFrameNavigation || !this._loader._isAvm2Enabled) { if (enterFrame) { - this._destructChildren(frameNum); - this._declareChildren(frameNum); + this._updateDisplayList(frameNum); this._enterFrame(frameNum); } this._constructChildren(); @@ -41068,9 +39493,6 @@ var MovieClipDefinition = function () { } } } - if (frameNum > this._totalFrames) { - return 1; - } if (frameNum > this._framesLoaded) { return this._framesLoaded; } @@ -41167,7 +39589,7 @@ var MovieClipDefinition = function () { if (element) { var soundStreamData = soundStream.data; var time = soundStream.seekIndex[frameNum] / soundStreamData.sampleRate / soundStreamData.channels; - if (this._framesLoaded >= this._totalFrames && !soundStream.channel) { + if (this._complete && !soundStream.channel) { var blob = new Blob(soundStream.rawFrames); element.preload = 'metadata'; element.loop = false; @@ -41238,7 +39660,7 @@ var MovieClipDefinition = function () { } }, get currentScene() { - return this._scenes ? this._scenes[this._currentScene] : new flash.display.Scene('', this.currentLabels, this._totalFrames); + return this._scenes ? this._scenes[this._currentScene] : new flash.display.Scene('', this.currentLabels, this._framesLoaded); }, get enabled() { return this._enabled; @@ -41254,7 +39676,7 @@ var MovieClipDefinition = function () { }, get scenes() { return this._scenes || [ - new flash.display.Scene('', this.currentLabels, this._totalFrames) + new flash.display.Scene('', this.currentLabels, this._framesLoaded) ]; }, get trackAsMenu() { @@ -41318,7 +39740,7 @@ var MovieClipDefinition = function () { } }, play: function () { - if (this._isPlaying || this._totalFrames <= 1) { + if (this._isPlaying || this._complete && this._totalFrames <= 1) { return; } this._isPlaying = true; @@ -41336,7 +39758,7 @@ var MovieClipDefinition = function () { } }, stop: function () { - if (!this._isPlaying || this._totalFrames <= 1) { + if (!this._isPlaying || this._complete && this._totalFrames <= 1) { return; } this._isPlaying = false; @@ -41446,6 +39868,61 @@ var SceneDefinition = function () { } }; }.call(this); +var ShaderDefinition = function () { + return { + __class__: 'flash.display.Shader', + initialize: function () { + this._data = null; + }, + __glue__: { + native: { + static: {}, + instance: { + data: { + get: function data() { + return this._data; + }, + set: function data(p) { + this._data = p; + } + }, + precisionHint: { + get: function precisionHint() { + return this._precisionHint; + }, + set: function precisionHint(p) { + this._precisionHint = p; + } + } + } + }, + script: { + instance: Glue.ALL + } + } + }; + }.call(this); +var ShaderDataDefinition = function () { + return { + __class__: 'flash.display.ShaderData', + initialize: function () { + this._byteCode = null; + }, + __glue__: { + native: { + static: {}, + instance: { + _setByteCode: function _setByteCode(code) { + this._byteCode = code; + } + } + }, + script: { + instance: Glue.ALL + } + } + }; + }.call(this); var ShapeDefinition = function () { var def = { __class__: 'flash.display.Shape', @@ -41455,7 +39932,9 @@ var ShapeDefinition = function () { var s = this.symbol; if (s && s.paths) { graphics._paths = s.paths; - finishShapePaths(s.paths, s.dictionary); + for (var i = 0; i < s.paths.length; i++) { + s.paths[i] = finishShapePath(s.paths[i], s.dictionary); + } graphics.bbox = s.bbox; graphics.strokeBbox = s.strokeBbox; graphics.dictionary = s.dictionary; @@ -41535,6 +40014,7 @@ var SimpleButtonDefinition = function () { this._prevAvm1StateCode = 0; this._avm1StateCode = 0; this._avm1MouseEvents = null; + this._isContainer = true; var s = this.symbol; if (s) { var states = s.states; @@ -41581,6 +40061,9 @@ var SimpleButtonDefinition = function () { state = this._downState; break; } + if (!state) { + return; + } var currentChild = this._children[0]; if (currentChild) { if (currentChild === state) { @@ -41591,6 +40074,10 @@ var SimpleButtonDefinition = function () { } currentChild._invalidateTransform(); } + if (!state) { + this._children.shift(); + return; + } this._children[0] = state; state._parent = this; state._invalidateTransform(); @@ -41847,6 +40334,7 @@ var SpriteDefinition = function () { var children = this._children; for (var i = 0; i < children.length; i++) { var displayListItem = children[i]; + Counter.count('constructChild'); if (flash.display.DisplayObject.class.isInstanceOf(displayListItem)) { displayListItem._index = i; } else { @@ -41879,7 +40367,7 @@ var SpriteDefinition = function () { } else { instance._dispatchEvent('load'); } - instance._dispatchEvent('added'); + instance._dispatchEvent('added', undefined, true); if (this._stage) { this._stage._addToStage(instance); } @@ -41945,37 +40433,7 @@ var SpriteDefinition = function () { var avm1Context = loader._avm1Context; var symbolProps = instance.symbol; if (symbolProps && symbolProps.variableName) { - var variableName = symbolProps.variableName; - var hasPath = variableName.lastIndexOf('.') >= 0 || variableName.lastIndexOf(':') >= 0; - var clip; - if (hasPath) { - var targetPath = variableName.split(/[.:\/]/g); - variableName = targetPath.pop(); - if (targetPath[0] == '_root' || targetPath[0] === '') { - clip = this.root._getAS2Object(); - targetPath.shift(); - if (targetPath[0] === '') { - targetPath.shift(); - } - } else { - clip = this._getAS2Object(); - } - while (targetPath.length > 0) { - var childName = targetPath.shift(); - clip = clip.asGetPublicProperty(childName) || clip[childName]; - if (!clip) { - throw new Error('Cannot find ' + childName + ' variable'); - } - } - } else { - clip = this._getAS2Object(); - } - if (!clip.asHasProperty(undefined, variableName, 0)) { - clip.asSetPublicProperty(variableName, instance.text); - } - instance._addEventListener('advanceFrame', function () { - instance.text = '' + clip.asGetPublicProperty(variableName); - }); + instance._getAS2Object().asSetPublicProperty('variable', symbolProps.variableName); } if (events) { var eventsBound = []; @@ -42014,7 +40472,7 @@ var SpriteDefinition = function () { }.bind(instance, eventsBound), false); } } - if (name) { + if (name && this._getAS2Object && instance._getAS2Object) { this._getAS2Object().asSetPublicProperty(name, instance._getAS2Object()); } }, @@ -42117,6 +40575,7 @@ var StageDefinition = function () { this._invalidObjects = []; this._mouseMoved = false; this._mouseTarget = this; + this._mouseEvents = []; this._cursor = 'auto'; this._concatenatedTransform.invalid = false; }, @@ -42219,7 +40678,7 @@ var StageDefinition = function () { var neighbours = this._qtree.retrieve(xMin, yMin, xMax, yMax); for (var j = 0; j < neighbours.length; j++) { var item = neighbours[j]; - if (item.obj._invalid || xMin > item.x + item.width || xMax < item.x || yMin > item.y + item.height || yMax < item.y) { + if (item.obj._invalid || xMin > item.xMax || xMax < item.xMin || yMin > item.yMax || yMax < item.yMin) { continue; } item.obj._invalid = true; @@ -42228,6 +40687,26 @@ var StageDefinition = function () { } return invalidPath; }, + _handleMouseButtons: function () { + if (this._mouseEvents.length === 0) { + return; + } + var eventType = this._mouseEvents.shift(); + switch (eventType) { + case 'mousedown': + if (this._mouseTarget._buttonMode) { + this._mouseTarget._gotoButtonState('down'); + } + this._mouseTarget._dispatchEvent('mouseDown'); + break; + case 'mouseup': + if (this._mouseTarget._buttonMode) { + this._mouseTarget._gotoButtonState('over'); + } + this._mouseTarget._dispatchEvent('mouseUp'); + break; + } + }, _handleMouse: function handleMouse() { var mouseX = this._mouseX; var mouseY = this._mouseY; @@ -42645,11 +41124,11 @@ var EventDispatcherDefinition = function () { rollOut: false, rollOver: false }; - function doDispatchEvent(dispatcher, event, eventClass) { + function doDispatchEvent(dispatcher, event, eventClass, bubbles) { var target = dispatcher._target; var type = event._type || event; var listeners = dispatcher._listeners[type]; - if (typeof event === 'string' && mouseEvents[event] || event._bubbles) { + if (bubbles || typeof event === 'string' && mouseEvents[event] || event._bubbles) { var ancestors = []; var currentNode = target._parent; while (currentNode) { @@ -42666,22 +41145,22 @@ var EventDispatcherDefinition = function () { while (i-- && keepPropagating) { var currentTarget = ancestors[i]; var queue = currentTarget._captureListeners[type]; - keepPropagating = processListeners(queue, event, null, target, currentTarget, 1); + keepPropagating = processListeners(queue, event, eventClass, bubbles, target, currentTarget, 1); } if (listeners && keepPropagating) { - keepPropagating = processListeners(listeners, event, null, target); + keepPropagating = processListeners(listeners, event, eventClass, bubbles, target); } for (var i = 0; i < ancestors.length && keepPropagating; i++) { var currentTarget = ancestors[i]; var queue = currentTarget._listeners[type]; - keepPropagating = processListeners(queue, event, null, target, currentTarget, 3); + keepPropagating = processListeners(queue, event, eventClass, bubbles, target, currentTarget, 3); } } else if (listeners) { - processListeners(listeners, event, null, target); + processListeners(listeners, event, eventClass, bubbles, target); } return !event._isDefaultPrevented; } - function processListeners(queue, event, eventClass, target, currentTarget, eventPhase) { + function processListeners(queue, event, eventClass, bubbles, target, currentTarget, eventPhase) { if (queue) { queue = queue.slice(); var needsInit = true; @@ -42791,8 +41270,8 @@ var EventDispatcherDefinition = function () { _hasEventListener: function hasEventListener(type) { return type in this._listeners || type in this._captureListeners; }, - _dispatchEvent: function dispatchEvent(event, eventClass) { - doDispatchEvent(this, event, eventClass); + _dispatchEvent: function dispatchEvent(event, eventClass, bubbles) { + doDispatchEvent(this, event, eventClass, bubbles); }, __glue__: { native: { @@ -43843,27 +42322,9 @@ var TransformDefinition = function () { } var target = this._target; target._invalidate(); - var a = val.a; - var b = val.b; - var c = val.c; - var d = val.d; - var tx = val.tx * 20 | 0; - var ty = val.ty * 20 | 0; - target._rotation = a !== 0 ? Math.atan(b / a) * 180 / Math.PI : b > 0 ? 90 : -90; - var sx = Math.sqrt(a * a + b * b); - target._scaleX = a > 0 ? sx : -sx; - var sy = Math.sqrt(d * d + c * c); - target._scaleY = d > 0 ? sy : -sy; - target._invalidateTransform(); - target._currentTransform = { - a: a, - b: b, - c: c, - d: d, - tx: tx, - ty: ty - }; + target._setTransformMatrix(val, true); target._current3DTransform = null; + target._animated = false; }, get matrix3D() { var m = this._target._current3DTransform; @@ -43880,7 +42341,6 @@ var TransformDefinition = function () { }, ctor: function (target) { this._target = target; - target._transform = this; } }; var desc = Object.getOwnPropertyDescriptor; @@ -43929,6 +42389,181 @@ var Vector3DDefinition = function () { }; }.call(this); } +var MicrophoneDefinition = function () { + return { + __class__: 'flash.media.Microphone', + initialize: function () { + }, + __glue__: { + native: { + static: { + getMicrophone: function getMicrophone(index) { + notImplemented('Microphone.getMicrophone'); + }, + getEnhancedMicrophone: function getEnhancedMicrophone(index) { + notImplemented('Microphone.getEnhancedMicrophone'); + }, + names: { + get: function names() { + notImplemented('Microphone.names'); + } + }, + isSupported: { + get: function isSupported() { + notImplemented('Microphone.isSupported'); + } + } + }, + instance: { + setSilenceLevel: function setSilenceLevel(silenceLevel, timeout) { + notImplemented('Microphone.setSilenceLevel'); + }, + setUseEchoSuppression: function setUseEchoSuppression(useEchoSuppression) { + notImplemented('Microphone.setUseEchoSuppression'); + }, + setLoopBack: function setLoopBack(state) { + notImplemented('Microphone.setLoopBack'); + }, + gain: { + set: function gain(gain) { + notImplemented('Microphone.gain'); + this._gain = gain; + }, + get: function gain() { + notImplemented('Microphone.gain'); + return this._gain; + } + }, + rate: { + set: function rate(rate) { + notImplemented('Microphone.rate'); + this._rate = rate; + }, + get: function rate() { + notImplemented('Microphone.rate'); + return this._rate; + } + }, + codec: { + set: function codec(codec) { + notImplemented('Microphone.codec'); + this._codec = codec; + }, + get: function codec() { + notImplemented('Microphone.codec'); + return this._codec; + } + }, + framesPerPacket: { + get: function framesPerPacket() { + notImplemented('Microphone.framesPerPacket'); + return this._framesPerPacket; + }, + set: function framesPerPacket(frames) { + notImplemented('Microphone.framesPerPacket'); + this._framesPerPacket = frames; + } + }, + encodeQuality: { + get: function encodeQuality() { + notImplemented('Microphone.encodeQuality'); + return this._encodeQuality; + }, + set: function encodeQuality(quality) { + notImplemented('Microphone.encodeQuality'); + this._encodeQuality = quality; + } + }, + noiseSuppressionLevel: { + get: function noiseSuppressionLevel() { + notImplemented('Microphone.noiseSuppressionLevel'); + return this._noiseSuppressionLevel; + }, + set: function noiseSuppressionLevel(level) { + notImplemented('Microphone.noiseSuppressionLevel'); + this._noiseSuppressionLevel = level; + } + }, + enableVAD: { + get: function enableVAD() { + notImplemented('Microphone.enableVAD'); + return this._enableVAD; + }, + set: function enableVAD(enable) { + notImplemented('Microphone.enableVAD'); + this._enableVAD = enable; + } + }, + activityLevel: { + get: function activityLevel() { + notImplemented('Microphone.activityLevel'); + return this._activityLevel; + } + }, + index: { + get: function index() { + notImplemented('Microphone.index'); + return this._index; + } + }, + muted: { + get: function muted() { + notImplemented('Microphone.muted'); + return this._muted; + } + }, + name: { + get: function name() { + notImplemented('Microphone.name'); + return this._name; + } + }, + silenceLevel: { + get: function silenceLevel() { + notImplemented('Microphone.silenceLevel'); + return this._silenceLevel; + } + }, + silenceTimeout: { + get: function silenceTimeout() { + notImplemented('Microphone.silenceTimeout'); + return this._silenceTimeout; + } + }, + useEchoSuppression: { + get: function useEchoSuppression() { + notImplemented('Microphone.useEchoSuppression'); + return this._useEchoSuppression; + } + }, + soundTransform: { + get: function soundTransform() { + notImplemented('Microphone.soundTransform'); + return this._soundTransform; + }, + set: function soundTransform(sndTransform) { + notImplemented('Microphone.soundTransform'); + this._soundTransform = sndTransform; + } + }, + enhancedOptions: { + get: function enhancedOptions() { + notImplemented('Microphone.enhancedOptions'); + return this._enhancedOptions; + }, + set: function enhancedOptions(options) { + notImplemented('Microphone.enhancedOptions'); + this._enhancedOptions = options; + } + } + } + }, + script: { + instance: Glue.ALL + } + } + }; + }.call(this); var PLAY_USING_AUDIO_TAG = true; var SoundDefinition = function () { function getAudioDescription(soundData, onComplete) { @@ -44344,8 +42979,6 @@ var SoundChannelDefinition = function () { function createAudioChannel(sampleRate, channels) { if (WebAudioChannel.isSupported) return new WebAudioChannel(sampleRate, channels); - else if (AudioDataChannel.isSupported) - return new AudioDataChannel(sampleRate, channels); else error('PCM data playback is not supported by the browser'); } @@ -44451,59 +43084,6 @@ WebAudioChannel.prototype = { WebAudioChannel.isSupported = function () { return typeof AudioContext !== 'undefined' || typeof webkitAudioContext != 'undefined'; }(); -function AudioDataChannel(sampleRate, channels) { - this.sampleRate = sampleRate; - this.channels = channels; -} -AudioDataChannel.prototype = { - start: function () { - var sampleRate = this.sampleRate; - var channels = this.channels; - var self = this; - var audio = new Audio(); - audio.mozSetup(channels, sampleRate); - var currentWritePosition = 0; - var prebufferSize = sampleRate * channels / 2; - var tail = null, tailPosition; - this.interval = setInterval(function () { - var written; - if (tail) { - written = audio.mozWriteAudio(tail.subarray(tailPosition)); - currentWritePosition += written; - tailPosition += written; - if (tailPosition < tail.length) { - return; - } - tail = null; - } - var currentPosition = audio.mozCurrentSampleOffset(); - var available = currentPosition + prebufferSize - currentWritePosition; - available -= available % channels; - if (available > 0) { - var soundData = new Float32Array(available); - self.requestData(soundData, available); - written = audio.mozWriteAudio(soundData); - if (written < soundData.length) { - tail = soundData; - tailPosition = written; - } - currentWritePosition += written; - } - }, 100); - }, - stop: function () { - clearInterval(this.interval); - }, - requestData: function (data, count) { - this.ondatarequested({ - data: data, - count: count - }); - } -}; -AudioDataChannel.isSupported = function () { - return 'mozSetup' in new Audio(); -}(); var SoundMixerDefinition = function () { var masterVolume = 1; var registeredChannels = []; @@ -44839,56 +43419,40 @@ var VideoDefinition = function () { return def; }.call(this); { - var LocalConnectionDefinition = function () { + var FileFilterDefinition = function () { return { - __class__: 'flash.net.LocalConnection', + __class__: 'flash.net.FileFilter', initialize: function () { + this._description = null; + this._extension = null; + this._macType = null; }, __glue__: { native: { static: {}, instance: { - close: function close() { - notImplemented('LocalConnection.close'); - }, - connect: function connect(connectionName) { - notImplemented('LocalConnection.connect'); - }, - send: function send(connectionName, methodName) { - notImplemented('LocalConnection.send'); - }, - allowDomain: function allowDomain() { - notImplemented('LocalConnection.allowDomain'); - }, - allowInsecureDomain: function allowInsecureDomain() { - notImplemented('LocalConnection.allowInsecureDomain'); - }, - domain: { - get: function domain() { - somewhatImplemented('LocalConnection.domain'); - var url = FileLoadingService.resolveUrl('/'); - var m = /:\/\/(.+?)[:?#\/]/.exec(url); - return m && m[1]; + description: { + get: function description() { + return this._description; + }, + set: function description(value) { + this._description = value; } }, - client: { - get: function client() { - notImplemented('LocalConnection.client'); - return this._client; + extension: { + get: function extension() { + return this._extension; }, - set: function client(client) { - notImplemented('LocalConnection.client'); - this._client = client; + set: function extension(value) { + this._extension = value; } }, - isPerUser: { - get: function isPerUser() { - notImplemented('LocalConnection.isPerUser'); - return this._isPerUser; + macType: { + get: function macType() { + return this._macType; }, - set: function isPerUser(newValue) { - notImplemented('LocalConnection.isPerUser'); - this._isPerUser = newValue; + set: function macType(value) { + this._macType = value; } } } @@ -44897,6 +43461,63 @@ var VideoDefinition = function () { }; }.call(this); } +var LocalConnectionDefinition = function () { + return { + __class__: 'flash.net.LocalConnection', + initialize: function () { + }, + __glue__: { + native: { + static: {}, + instance: { + close: function close() { + notImplemented('LocalConnection.close'); + }, + connect: function connect(connectionName) { + notImplemented('LocalConnection.connect'); + }, + send: function send(connectionName, methodName) { + notImplemented('LocalConnection.send'); + }, + allowDomain: function allowDomain() { + notImplemented('LocalConnection.allowDomain'); + }, + allowInsecureDomain: function allowInsecureDomain() { + notImplemented('LocalConnection.allowInsecureDomain'); + }, + domain: { + get: function domain() { + somewhatImplemented('LocalConnection.domain'); + var url = FileLoadingService.resolveUrl('/'); + var m = /:\/\/(.+?)[:?#\/]/.exec(url); + return m && m[1]; + } + }, + client: { + get: function client() { + notImplemented('LocalConnection.client'); + return this._client; + }, + set: function client(client) { + notImplemented('LocalConnection.client'); + this._client = client; + } + }, + isPerUser: { + get: function isPerUser() { + notImplemented('LocalConnection.isPerUser'); + return this._isPerUser; + }, + set: function isPerUser(newValue) { + notImplemented('LocalConnection.isPerUser'); + this._isPerUser = newValue; + } + } + } + } + } + }; + }.call(this); var NetConnectionDefinition = function () { return { __class__: 'flash.net.NetConnection', @@ -45195,12 +43816,12 @@ var NetStreamDefinition = function () { request._checkPolicyFile = this._checkPolicyFile; var stream = new flash.net.URLStream(); stream._addEventListener('httpStatus', function (e) { - var responseHeaders = e[Multiname.getPublicQualifiedName('responseHeaders')]; + var responseHeaders = e.asGetPublicProperty('responseHeaders'); var contentTypeHeader = responseHeaders.filter(function (h) { - return h[Multiname.getPublicQualifiedName('name')] === 'Content-Type'; + return h.asGetPublicProperty('name') === 'Content-Type'; })[0]; - if (contentTypeHeader && contentTypeHeader[Multiname.getPublicQualifiedName('value')] !== 'application/octet-stream') { - this._contentTypeHint = contentTypeHeader[Multiname.getPublicQualifiedName('value')]; + if (contentTypeHeader && contentTypeHeader.asGetPublicProperty('value') !== 'application/octet-stream') { + this._contentTypeHint = contentTypeHeader.asGetPublicProperty('value'); } }.bind(this)); stream._addEventListener('progress', function (e) { @@ -45496,14 +44117,16 @@ var SharedObjectDefinition = function () { var simulated = false, result; switch (index) { case 4: - result = JSON.stringify(this._data).length; + result = JSON.stringify(this._data).length - 2; simulated = true; break; case 6: this._data = {}; + sessionStorage.removeItem(this._path); simulated = true; break; case 2: + sessionStorage.setItem(this._path, JSON.stringify(this._data)); simulated = true; result = true; break; @@ -45517,10 +44140,9 @@ var SharedObjectDefinition = function () { return { __class__: 'flash.net.SharedObject', initialize: function () { - this._data = {}; + this._path = null; + this._data = null; this._objectEncoding = _defaultObjectEncoding; - this._data[Multiname.getPublicQualifiedName('levelCompleted')] = 32; - this._data[Multiname.getPublicQualifiedName('completeLevels')] = 32; TelemetryService.reportTelemetry({ topic: 'feature', feature: SHAREDOBJECT_FEATURE @@ -45536,8 +44158,15 @@ var SharedObjectDefinition = function () { notImplemented('SharedObject.getDiskUsage'); }, getLocal: function getLocal(name, localPath, secure) { - var path = localPath + '/' + name; - return sharedObjects[path] || (sharedObjects[path] = new flash.net.SharedObject()); + var path = (localPath || '') + '/' + name; + if (sharedObjects[path]) { + return sharedObjects[path]; + } + var so = new flash.net.SharedObject(); + so._path = path; + var data = sessionStorage.getItem(path); + so._data = data ? JSON.parse(data) : {}; + return so; }, getRemote: function getRemote(name, remotePath, persistence, secure) { notImplemented('SharedObject.getRemote'); @@ -45553,7 +44182,7 @@ var SharedObjectDefinition = function () { }, instance: { setDirty: function setDirty(propertyName) { - notImplemented('SharedObject.setDirty'); + somewhatImplemented('SharedObject.setDirty'); }, invoke: function invoke(index) { return invokeWithArgsArray.call(this, index, Array.prototype.slice.call(arguments, 1)); @@ -45589,6 +44218,154 @@ var SharedObjectDefinition = function () { } }; }.call(this); +var SocketDefinition = function () { + return { + __class__: 'flash.net.Socket', + initialize: function () { + }, + __glue__: { + native: { + static: {}, + instance: { + internalGetSecurityErrorMessage: function internalGetSecurityErrorMessage(host, port) { + notImplemented('Socket.internalGetSecurityErrorMessage'); + }, + internalConnect: function internalConnect(host, port) { + notImplemented('Socket.internalConnect'); + }, + didFailureOccur: function didFailureOccur() { + notImplemented('Socket.didFailureOccur'); + }, + readBytes: function readBytes(bytes, offset, length) { + notImplemented('Socket.readBytes'); + }, + writeBytes: function writeBytes(bytes, offset, length) { + notImplemented('Socket.writeBytes'); + }, + writeBoolean: function writeBoolean(value) { + notImplemented('Socket.writeBoolean'); + }, + writeByte: function writeByte(value) { + notImplemented('Socket.writeByte'); + }, + writeShort: function writeShort(value) { + notImplemented('Socket.writeShort'); + }, + writeInt: function writeInt(value) { + notImplemented('Socket.writeInt'); + }, + writeUnsignedInt: function writeUnsignedInt(value) { + notImplemented('Socket.writeUnsignedInt'); + }, + writeFloat: function writeFloat(value) { + notImplemented('Socket.writeFloat'); + }, + writeDouble: function writeDouble(value) { + notImplemented('Socket.writeDouble'); + }, + writeMultiByte: function writeMultiByte(value, charSet) { + notImplemented('Socket.writeMultiByte'); + }, + writeUTF: function writeUTF(value) { + notImplemented('Socket.writeUTF'); + }, + writeUTFBytes: function writeUTFBytes(value) { + notImplemented('Socket.writeUTFBytes'); + }, + readBoolean: function readBoolean() { + notImplemented('Socket.readBoolean'); + }, + readByte: function readByte() { + notImplemented('Socket.readByte'); + }, + readUnsignedByte: function readUnsignedByte() { + notImplemented('Socket.readUnsignedByte'); + }, + readShort: function readShort() { + notImplemented('Socket.readShort'); + }, + readUnsignedShort: function readUnsignedShort() { + notImplemented('Socket.readUnsignedShort'); + }, + readInt: function readInt() { + notImplemented('Socket.readInt'); + }, + readUnsignedInt: function readUnsignedInt() { + notImplemented('Socket.readUnsignedInt'); + }, + readFloat: function readFloat() { + notImplemented('Socket.readFloat'); + }, + readDouble: function readDouble() { + notImplemented('Socket.readDouble'); + }, + readMultiByte: function readMultiByte(length, charSet) { + notImplemented('Socket.readMultiByte'); + }, + readUTF: function readUTF() { + notImplemented('Socket.readUTF'); + }, + readUTFBytes: function readUTFBytes(length) { + notImplemented('Socket.readUTFBytes'); + }, + internalClose: function internalClose() { + notImplemented('Socket.internalClose'); + }, + flush: function flush() { + notImplemented('Socket.flush'); + }, + writeObject: function writeObject(object) { + notImplemented('Socket.writeObject'); + }, + readObject: function readObject() { + notImplemented('Socket.readObject'); + }, + bytesAvailable: { + get: function bytesAvailable() { + notImplemented('Socket.bytesAvailable'); + return this._bytesAvailable; + } + }, + connected: { + get: function connected() { + notImplemented('Socket.connected'); + return this._connected; + } + }, + objectEncoding: { + get: function objectEncoding() { + notImplemented('Socket.objectEncoding'); + return this._objectEncoding; + }, + set: function objectEncoding(version) { + notImplemented('Socket.objectEncoding'); + this._objectEncoding = version; + } + }, + endian: { + get: function endian() { + notImplemented('Socket.endian'); + return this._endian; + }, + set: function endian(type) { + notImplemented('Socket.endian'); + this._endian = type; + } + }, + bytesPending: { + get: function bytesPending() { + notImplemented('Socket.bytesPending'); + return this._bytesPending; + } + } + } + }, + script: { + instance: Glue.ALL + } + } + }; + }.call(this); var URLLoaderDefinition = function () { return { initialize: function () { @@ -45763,7 +44540,7 @@ var URLStreamDefinition = function () { httpStatus ]); var headers = []; - httpHeaders.split(/\r\n/g).forEach(function (h) { + httpHeaders.split(/(?:\n|\r?\n)/g).forEach(function (h) { var m = /^([^:]+): (.*)$/.exec(h); if (m) { headers.push(URLRequestHeaderClass.createInstance([ @@ -45775,8 +44552,8 @@ var URLStreamDefinition = function () { } } }); - httpStatusEvent.public$responseHeaders = headers; - httpStatusEvent.public$responseURL = location; + httpStatusEvent.asSetPublicProperty('responseHeaders', headers); + httpStatusEvent.asSetPublicProperty('responseURL', location); self._dispatchEvent(httpStatusEvent); }; session.onclose = function () { @@ -45921,12 +44698,12 @@ var URLStreamDefinition = function () { }, instance: { ctor: function ctor(parentDomainOrNativeObject) { - if (parentDomainOrNativeObject instanceof Domain) { + if (parentDomainOrNativeObject instanceof ApplicationDomain) { this.nativeObject = parentDomainOrNativeObject; return; } var parentNativeObject = parentDomainOrNativeObject ? parentDomainOrNativeObject.nativeObject : AVM2.currentDomain().system; - this.nativeObject = new Domain(parentNativeObject.vm, parentNativeObject); + this.nativeObject = new ApplicationDomain(parentNativeObject.vm, parentNativeObject); }, getDefinition: function getDefinition(name) { var simpleName = name.replace('::', '.'); @@ -46027,6 +44804,24 @@ var CapabilitiesDefinition = function () { get: function screenResolutionY() { return window.screen.height; } + }, + manufacturer: { + get: function manufacturer() { + somewhatImplemented('Capabilities.manufacturer'); + return 'Mozilla Research'; + } + }, + language: { + get: function language() { + somewhatImplemented('Capabilities.language'); + return 'en'; + } + }, + playerType: { + get: function playerType() { + somewhatImplemented('Capabilities.playerType'); + return 'PlugIn'; + } } } }, @@ -46079,7 +44874,7 @@ var SecurityDefinition = function () { somewhatImplemented('Security.allowInsecureDomain'); }, loadPolicyFile: function loadPolicyFile(url) { - notImplemented('Security.loadPolicyFile'); + somewhatImplemented('Security.loadPolicyFile'); }, duplicateSandboxBridgeInputArguments: function duplicateSandboxBridgeInputArguments(toplevel, args) { notImplemented('Security.duplicateSandboxBridgeInputArguments'); @@ -46219,7 +45014,8 @@ var SystemDefinition = function () { }, useCodePage: { get: function useCodePage() { - notImplemented('System.useCodePage'); + somewhatImplemented('System.useCodePage'); + return false; }, set: function useCodePage(value) { notImplemented('System.useCodePage'); @@ -46227,8 +45023,25 @@ var SystemDefinition = function () { }, vmVersion: { get: function vmVersion() { - notImplemented('System.vmVersion'); + somewhatImplemented('System.vmVersion'); + return '1.0 shumway'; } + }, + swfVersion: { + get: function () { + return 19; + } + }, + apiVersion: { + get: function () { + return 26; + } + }, + getArgv: function () { + return []; + }, + getRunmode: function () { + return 'mixed'; } }, instance: {} @@ -49397,6 +48210,42 @@ var StaticTextDefinition = function () { }; return def; }.call(this); +var StyleSheetDefinition = function () { + return { + __class__: 'flash.text.StyleSheet', + initialize: function () { + }, + __glue__: { + native: { + static: {}, + instance: { + _update: function _update() { + somewhatImplemented('StyleSheet._update'); + }, + _parseCSSInternal: function _parseCSSInternal(cssText) { + somewhatImplemented('StyleSheet._parseCSSInternal'); + return null; + }, + _parseCSSFontFamily: function _parseCSSFontFamily(fontFamily) { + notImplemented('StyleSheet._parseCSSFontFamily'); + }, + _parseColor: function _parseColor(color) { + notImplemented('StyleSheet._parseColor'); + }, + _styles: { + get: function _styles() { + return this.__styles; + }, + set: function _styles(styles) { + somewhatImplemented('StyleSheet._styles'); + this.__styles = styles; + } + } + } + } + } + }; + }.call(this); var TextFieldDefinition = function () { var htmlParser = document.createElement('p'); var measureCtx = document.createElement('canvas').getContext('2d'); @@ -49449,7 +48298,7 @@ var TextFieldDefinition = function () { } function createTrunk(initialFormat) { var trunk = { - type: 'P', + type: 'SPAN', format: { ALIGN: initialFormat.align }, @@ -49501,6 +48350,14 @@ var TextFieldDefinition = function () { } var nodeType = input.localName.toUpperCase(); if (!knownNodeTypes[nodeType] || multiline === false && (nodeType === 'P' || nodeType === 'BR')) { + if (nodeType === 'SBR') { + destinationList.push({ + type: 'BR', + text: null, + format: null, + children: null + }); + } convertNodeList(input.childNodes, destinationList, content, multiline); return; } @@ -49536,20 +48393,23 @@ var TextFieldDefinition = function () { var blockNode = false; switch (node.type) { case 'plain-text': - for (var i = 0; i < node.lines.length; i++) { - addRunsForText(state, node.lines[i]); - finishLine(state); + var lines = node.lines; + for (var i = 0; i < lines.length; i++) { + addRunsForText(state, lines[i]); + if (i < lines.length - 1) { + finishLine(state, true); + } } return; case 'text': addRunsForText(state, node.text); return; case 'BR': - finishLine(state); + finishLine(state, true); return; case 'LI': case 'P': - finishLine(state); + finishLine(state, false); pushFormat(state, node); blockNode = true; break; @@ -49574,7 +48434,7 @@ var TextFieldDefinition = function () { popFormat(state); } if (blockNode) { - finishLine(state); + finishLine(state, true); } } var WRAP_OPPORTUNITIES = { @@ -49595,7 +48455,7 @@ var TextFieldDefinition = function () { var width = state.ctx.measureText(text).width; var availableWidth = state.w - state.line.width; if (availableWidth <= 0) { - finishLine(state); + finishLine(state, false); availableWidth = state.w - state.line.width; } if (width <= availableWidth) { @@ -49616,7 +48476,7 @@ var TextFieldDefinition = function () { } if (wrapOffset === -1) { if (state.line.width > 0) { - finishLine(state); + finishLine(state, false); continue; } while (state.ctx.measureText(text.substr(0, offset)).width > availableWidth) { @@ -49631,7 +48491,7 @@ var TextFieldDefinition = function () { width = state.ctx.measureText(runText).width; addTextRun(state, runText, width); if (state.wordWrap) { - finishLine(state); + finishLine(state, false); } text = text.substr(wrapOffset); } @@ -49659,9 +48519,13 @@ var TextFieldDefinition = function () { line.largestFormat = format; } } - function finishLine(state) { + function finishLine(state, forceNewline) { var line = state.line; if (line.runs.length === 0) { + if (forceNewline) { + var format = state.currentFormat; + state.line.y += format.font._metrics.height * format.size + format.leading | 0; + } return; } var runs = line.runs; @@ -49959,10 +48823,6 @@ var TextFieldDefinition = function () { this._invalidateBounds(); this._dimensionsValid = false; }, - _getRegion: function getRegion(targetCoordSpace) { - this.ensureDimensions(); - return this._getTransformedRect(this._getContentBounds(), targetCoordSpace); - }, ensureDimensions: function () { if (this._dimensionsValid) { return; @@ -49996,6 +48856,7 @@ var TextFieldDefinition = function () { embedFonts: this._embedFonts }; collectRuns(this._content.tree, state); + finishLine(state, false); this._textWidth = state.maxLineWidth | 0; this._textHeight = state.line.y | 0; this._lines = state.lines; @@ -50063,10 +48924,24 @@ var TextFieldDefinition = function () { return this._content.text; }, set text(val) { + val = String(val); if (this._content && this._content.text === val) { return; } - var lines = val.split('\r\n').join('\n').split('\r').join('\n').split('\n'); + var lines = []; + var lineOffset = 0; + for (var index = 0; index < val.length;) { + var char = val[index]; + if (char === '\r' || char === '\n') { + lines.push(val.substring(lineOffset, index)); + lineOffset = index; + if (char === '\r' && val[index + 1] === '\n') { + index++; + } + } + index++; + } + lines.push(val.substring(lineOffset, index)); this._content = { tree: createTrunk(this._defaultTextFormat), text: val, @@ -50085,6 +48960,8 @@ var TextFieldDefinition = function () { if (this._htmlText === val) { return; } + this._defaultTextFormat.bold = false; + this._defaultTextFormat.italic = false; this._content = parseHtml(val, this._defaultTextFormat, this._multiline); this.invalidateDimensions(); }, @@ -50100,6 +48977,11 @@ var TextFieldDefinition = function () { }, setTextFormat: function (format, beginIndex, endIndex) { this.defaultTextFormat = format; + if (this._content && this._content.text === this._content.htmlText) { + var text = this.text; + this._content = null; + this.text = text; + } this.invalidateDimensions(); }, get x() { @@ -50136,6 +49018,27 @@ var TextFieldDefinition = function () { } this._bbox.yMax = value; this._invalidate(); + }, + _getContentBounds: function () { + this.ensureDimensions(); + return this._bbox; + }, + _getRegion: function getRegion(targetCoordSpace) { + return this._getTransformedRect(this._getContentBounds(), targetCoordSpace); + }, + getLineMetrics: function (lineIndex) { + this.ensureDimensions(); + if (lineIndex < 0 || lineIndex >= this._lines.length) { + throwError('RangeError', Errors.ParamRangeError); + } + var line = this._lines[lineIndex]; + var format = line.largestFormat; + var metrics = format.font._metrics; + var size = format.size; + var ascent = metrics.ascent * size + 0.49999 | 0; + var descent = metrics.descent * size + 0.49999 | 0; + var leading = metrics.leading * size + 0.49999 + line.leading | 0; + return new flash.text.TextLineMetrics(line.x + 2, line.width, line.height, ascent, descent, leading); } }; var desc = Object.getOwnPropertyDescriptor; @@ -50230,18 +49133,10 @@ var TextFieldDefinition = function () { } }, getLineMetrics: function (lineIndex) { - this.ensureDimensions(); - if (lineIndex < 0 || lineIndex >= this._lines.length) { - throwError('RangeError', Errors.ParamRangeError); - } - var line = this._lines[lineIndex]; - var format = line.largestFormat; - var metrics = format.font._metrics; - var size = format.size; - var ascent = metrics.ascent * size + 0.49999 | 0; - var descent = metrics.descent * size + 0.49999 | 0; - var leading = metrics.leading * size + 0.49999 + line.leading | 0; - return new flash.text.TextLineMetrics(line.x + 2, line.width, line.height, ascent, descent, leading); + return this.getLineMetrics(lineIndex); + }, + setSelection: function (beginIndex, endIndex) { + somewhatImplemented('TextField.setSelection'); }, scrollV: { get: function scrollV() { @@ -50377,6 +49272,7 @@ var TextFieldDefinition = function () { return def; }.call(this); var TextFormatDefinition = function () { + var measureTextField; return { __class__: 'flash.text.TextFormat', initialize: function () { @@ -50414,6 +49310,32 @@ var TextFormatDefinition = function () { leading: this._leading || 0 }; }, + as2GetTextExtent: function (text, width) { + if (!measureTextField) { + measureTextField = new flash.text.TextField(); + measureTextField._multiline = true; + } + if (!isNaN(width) && width > 0) { + measureTextField.width = width + 4; + measureTextField._wordWrap = true; + } else { + measureTextField._wordWrap = false; + } + measureTextField.defaultTextFormat = this; + measureTextField.text = text; + measureTextField.ensureDimensions(); + var result = {}; + var textWidth = measureTextField._textWidth; + var textHeight = measureTextField._textHeight; + result.asSetPublicProperty('width', textWidth); + result.asSetPublicProperty('height', textHeight); + result.asSetPublicProperty('textFieldWidth', textWidth + 4); + result.asSetPublicProperty('textFieldHeight', textHeight + 4); + var metrics = measureTextField.getLineMetrics(0); + result.asSetPublicProperty('ascent', metrics.asGetPublicProperty('ascent')); + result.asSetPublicProperty('descent', metrics.asGetPublicProperty('descent')); + return result; + }, __glue__: { native: { static: {}, @@ -50575,6 +49497,257 @@ var TextFormatDefinition = function () { } }; }.call(this); +var ContentElementDefinition = function () { + return { + __class__: 'flash.text.engine.ContentElement', + initialize: function () { + }, + __glue__: { + native: { + static: {}, + instance: { + textBlock: { + get: function textBlock() { + notImplemented('ContentElement.textBlock'); + return this._textBlock; + } + }, + textBlockBeginIndex: { + get: function textBlockBeginIndex() { + notImplemented('ContentElement.textBlockBeginIndex'); + return this._textBlockBeginIndex; + } + }, + elementFormat: { + get: function elementFormat() { + return this._elementFormat; + }, + set: function elementFormat(value) { + somewhatImplemented('ContentElement.elementFormat'); + this._elementFormat = value; + } + }, + eventMirror: { + get: function eventMirror() { + return this._eventMirror; + }, + set: function eventMirror(value) { + somewhatImplemented('ContentElement.eventMirror'); + this._eventMirror = value; + } + }, + groupElement: { + get: function groupElement() { + notImplemented('ContentElement.groupElement'); + return this._groupElement; + } + }, + rawText: { + get: function rawText() { + notImplemented('ContentElement.rawText'); + return this._rawText; + } + }, + text: { + get: function text() { + notImplemented('ContentElement.text'); + return this._text; + } + }, + textRotation: { + get: function textRotation() { + return this._textRotation; + }, + set: function textRotation(value) { + somewhatImplemented('ContentElement.textRotation'); + this._textRotation = value; + } + } + } + } + } + }; + }.call(this); +var ElementFormatDefinition = function () { + return { + __class__: 'flash.text.engine.ElementFormat', + initialize: function () { + }, + __glue__: { + native: { + static: {}, + instance: { + getFontMetrics: function getFontMetrics() { + notImplemented('ElementFormat.getFontMetrics'); + }, + alignmentBaseline: { + get: function alignmentBaseline() { + return this._alignmentBaseline; + }, + set: function alignmentBaseline(alignmentBaseline) { + somewhatImplemented('ElementFormat.alignmentBaseline'); + this._alignmentBaseline = alignmentBaseline; + } + }, + alpha: { + get: function alpha() { + return this._alpha; + }, + set: function alpha(value) { + somewhatImplemented('ElementFormat.alpha'); + this._alpha = value; + } + }, + baselineShift: { + get: function baselineShift() { + return this._baselineShift; + }, + set: function baselineShift(value) { + somewhatImplemented('ElementFormat.baselineShift'); + this._baselineShift = value; + } + }, + breakOpportunity: { + get: function breakOpportunity() { + return this._breakOpportunity; + }, + set: function breakOpportunity(opportunityType) { + somewhatImplemented('ElementFormat.breakOpportunity'); + this._breakOpportunity = opportunityType; + } + }, + color: { + get: function color() { + return this._color; + }, + set: function color(value) { + somewhatImplemented('ElementFormat.color'); + this._color = value; + } + }, + dominantBaseline: { + get: function dominantBaseline() { + return this._dominantBaseline; + }, + set: function dominantBaseline(dominantBaseline) { + somewhatImplemented('ElementFormat.dominantBaseline'); + this._dominantBaseline = dominantBaseline; + } + }, + fontDescription: { + get: function fontDescription() { + return this._fontDescription; + }, + set: function fontDescription(value) { + somewhatImplemented('ElementFormat.fontDescription'); + this._fontDescription = value; + } + }, + digitCase: { + get: function digitCase() { + return this._digitCase; + }, + set: function digitCase(digitCaseType) { + somewhatImplemented('ElementFormat.digitCase'); + this._digitCase = digitCaseType; + } + }, + digitWidth: { + get: function digitWidth() { + return this._digitWidth; + }, + set: function digitWidth(digitWidthType) { + somewhatImplemented('ElementFormat.digitWidth'); + this._digitWidth = digitWidthType; + } + }, + ligatureLevel: { + get: function ligatureLevel() { + return this._ligatureLevel; + }, + set: function ligatureLevel(ligatureLevelType) { + somewhatImplemented('ElementFormat.ligatureLevel'); + this._ligatureLevel = ligatureLevelType; + } + }, + fontSize: { + get: function fontSize() { + return this._fontSize; + }, + set: function fontSize(value) { + somewhatImplemented('ElementFormat.fontSize'); + this._fontSize = value; + } + }, + kerning: { + get: function kerning() { + return this._kerning; + }, + set: function kerning(value) { + somewhatImplemented('ElementFormat.kerning'); + this._kerning = value; + } + }, + locale: { + get: function locale() { + return this._locale; + }, + set: function locale(value) { + somewhatImplemented('ElementFormat.locale'); + this._locale = value; + } + }, + textRotation: { + get: function textRotation() { + return this._textRotation; + }, + set: function textRotation(value) { + somewhatImplemented('ElementFormat.textRotation'); + this._textRotation = value; + } + }, + trackingRight: { + get: function trackingRight() { + return this._trackingRight; + }, + set: function trackingRight(value) { + somewhatImplemented('ElementFormat.trackingRight'); + this._trackingRight = value; + } + }, + trackingLeft: { + get: function trackingLeft() { + return this._trackingLeft; + }, + set: function trackingLeft(value) { + somewhatImplemented('ElementFormat.trackingLeft'); + this._trackingLeft = value; + } + }, + typographicCase: { + get: function typographicCase() { + return this._typographicCase; + }, + set: function typographicCase(typographicCaseType) { + somewhatImplemented('ElementFormat.typographicCase'); + this._typographicCase = typographicCaseType; + } + }, + locked: { + get: function locked() { + notImplemented('ElementFormat.locked'); + return this._locked; + }, + set: function locked(value) { + notImplemented('ElementFormat.locked'); + this._locked = value; + } + } + } + } + } + }; + }.call(this); var FontDescriptionDefinition = function () { return { __class__: 'flash.text.engine.FontDescription', @@ -50593,61 +49766,55 @@ var FontDescriptionDefinition = function () { instance: { renderingMode: { get: function renderingMode() { - notImplemented('FontDescription.renderingMode'); return this._renderingMode; }, set: function renderingMode(value) { - notImplemented('FontDescription.renderingMode'); + somewhatImplemented('FontDescription.renderingMode'); this._renderingMode = value; } }, fontLookup: { get: function fontLookup() { - notImplemented('FontDescription.fontLookup'); return this._fontLookup; }, set: function fontLookup(value) { - notImplemented('FontDescription.fontLookup'); + somewhatImplemented('FontDescription.fontLookup'); this._fontLookup = value; } }, fontName: { get: function fontName() { - notImplemented('FontDescription.fontName'); return this._fontName; }, set: function fontName(value) { - notImplemented('FontDescription.fontName'); + somewhatImplemented('FontDescription.fontName'); this._fontName = value; } }, fontPosture: { get: function fontPosture() { - notImplemented('FontDescription.fontPosture'); return this._fontPosture; }, set: function fontPosture(value) { - notImplemented('FontDescription.fontPosture'); + somewhatImplemented('FontDescription.fontPosture'); this._fontPosture = value; } }, fontWeight: { get: function fontWeight() { - notImplemented('FontDescription.fontWeight'); return this._fontWeight; }, set: function fontWeight(value) { - notImplemented('FontDescription.fontWeight'); + somewhatImplemented('FontDescription.fontWeight'); this._fontWeight = value; } }, cffHinting: { get: function cffHinting() { - notImplemented('FontDescription.cffHinting'); return this._cffHinting; }, set: function cffHinting(value) { - notImplemented('FontDescription.cffHinting'); + somewhatImplemented('FontDescription.cffHinting'); this._cffHinting = value; } }, @@ -50670,6 +49837,499 @@ var FontDescriptionDefinition = function () { } }; }.call(this); +var GroupElementDefinition = function () { + return { + __class__: 'flash.text.engine.GroupElement', + initialize: function () { + }, + __glue__: { + native: { + static: {}, + instance: { + getElementAt: function getElementAt(index) { + notImplemented('GroupElement.getElementAt'); + }, + setElements: function setElements(value) { + somewhatImplemented('GroupElement.setElements'); + this._elements = value; + }, + groupElements: function groupElements(beginIndex, endIndex) { + notImplemented('GroupElement.groupElements'); + }, + ungroupElements: function ungroupElements(groupIndex) { + notImplemented('GroupElement.ungroupElements'); + }, + mergeTextElements: function mergeTextElements(beginIndex, endIndex) { + notImplemented('GroupElement.mergeTextElements'); + }, + splitTextElement: function splitTextElement(elementIndex, splitIndex) { + notImplemented('GroupElement.splitTextElement'); + }, + replaceElements: function replaceElements(beginIndex, endIndex, newElements) { + notImplemented('GroupElement.replaceElements'); + }, + getElementAtCharIndex: function getElementAtCharIndex(charIndex) { + notImplemented('GroupElement.getElementAtCharIndex'); + }, + elementCount: { + get: function elementCount() { + notImplemented('GroupElement.elementCount'); + return this._elementCount; + } + } + } + } + } + }; + }.call(this); +var SpaceJustifierDefinition = function () { + return { + __class__: 'flash.text.engine.SpaceJustifier', + initialize: function () { + this._letterSpacing = false; + this._optimumSpacing = 1; + this._minimumSpacing = 0.5; + this._maximumSpacing = 1.5; + }, + __glue__: { + native: { + static: {}, + instance: { + cloneSpacing: function cloneSpacing(justifier) { + somewhatImplemented('SpaceJustifier.cloneSpacing'); + justifier._optimumSpacing = this._optimumSpacing; + justifier._minimumSpacing = this._minimumSpacing; + justifier._maximumSpacing = this._maximumSpacing; + }, + letterSpacing: { + get: function letterSpacing() { + return this._letterSpacing; + }, + set: function letterSpacing(value) { + somewhatImplemented('SpaceJustifier.letterSpacing'); + this._letterSpacing = value; + } + }, + minimumSpacing: { + get: function minimumSpacing() { + return this._minimumSpacing; + }, + set: function minimumSpacing(value) { + somewhatImplemented('SpaceJustifier.minimumSpacing'); + this._minimumSpacing = value; + } + }, + optimumSpacing: { + get: function optimumSpacing() { + return this._optimumSpacing; + }, + set: function optimumSpacing(value) { + somewhatImplemented('SpaceJustifier.optimumSpacing'); + this._optimumSpacing = value; + } + }, + maximumSpacing: { + get: function maximumSpacing() { + return this._maximumSpacing; + }, + set: function maximumSpacing(value) { + somewhatImplemented('SpaceJustifier.maximumSpacing'); + this._maximumSpacing = value; + } + } + } + } + } + }; + }.call(this); +var TextBlockDefinition = function () { + return { + __class__: 'flash.text.engine.TextBlock', + initialize: function () { + this._firstLine = null; + this._lastLine = null; + }, + __glue__: { + native: { + static: {}, + instance: { + getTextJustifier: function getTextJustifier() { + return this._textJustifier; + }, + setTextJustifier: function setTextJustifier(value) { + somewhatImplemented('TextBlock.setTextJustifier'); + this._textJustifier = value; + }, + getTabStops: function getTabStops() { + return this._tabStops; + }, + setTabStops: function setTabStops(value) { + somewhatImplemented('TextBlock.setTabStops'); + this._tabStops = value; + }, + findNextAtomBoundary: function findNextAtomBoundary(afterCharIndex) { + notImplemented('TextBlock.findNextAtomBoundary'); + }, + findPreviousAtomBoundary: function findPreviousAtomBoundary(beforeCharIndex) { + notImplemented('TextBlock.findPreviousAtomBoundary'); + }, + findNextWordBoundary: function findNextWordBoundary(afterCharIndex) { + notImplemented('TextBlock.findNextWordBoundary'); + }, + findPreviousWordBoundary: function findPreviousWordBoundary(beforeCharIndex) { + notImplemented('TextBlock.findPreviousWordBoundary'); + }, + getTextLineAtCharIndex: function getTextLineAtCharIndex(charIndex) { + notImplemented('TextBlock.getTextLineAtCharIndex'); + }, + DoCreateTextLine: function DoCreateTextLine(previousLine, width, lineOffset, fitSomething, reuseLine) { + somewhatImplemented('TextBlock.DoCreateTextLine'); + if (previousLine) { + return null; + } + var textLine = new flash.text.engine.TextLine(); + textLine._textBlock = this; + textLine._specifiedWidth = width; + textLine._rawTextLength = 0; + textLine._textWidth = 0; + textLine._textHeight = 0; + textLine._ascent = 0; + textLine._descent = 0; + textLine._unjustifiedTextWidth = 0; + textLine._validity = 'valid'; + textLine._previousLine = null; + textLine._nextLine = null; + this._firstLine = textLine; + this._lastLine = textLine; + return textLine; + }, + releaseLineCreationData: function releaseLineCreationData() { + notImplemented('TextBlock.releaseLineCreationData'); + }, + releaseLines: function releaseLines(firstLine, lastLine) { + notImplemented('TextBlock.releaseLines'); + }, + dump: function dump() { + notImplemented('TextBlock.dump'); + }, + applyNonLinearFontScaling: { + get: function applyNonLinearFontScaling() { + return this._applyNonLinearFontScaling; + }, + set: function applyNonLinearFontScaling(value) { + somewhatImplemented('TextBlock.applyNonLinearFontScaling'); + this._applyNonLinearFontScaling = value; + } + }, + baselineFontDescription: { + get: function baselineFontDescription() { + return this._baselineFontDescription; + }, + set: function baselineFontDescription(value) { + somewhatImplemented('TextBlock.baselineFontDescription'); + this._baselineFontDescription = value; + } + }, + baselineFontSize: { + get: function baselineFontSize() { + return this._baselineFontSize; + }, + set: function baselineFontSize(value) { + somewhatImplemented('TextBlock.baselineFontSize'); + this._baselineFontSize = value; + } + }, + baselineZero: { + get: function baselineZero() { + return this._baselineZero; + }, + set: function baselineZero(value) { + somewhatImplemented('TextBlock.baselineZero'); + this._baselineZero = value; + } + }, + content: { + get: function content() { + return this._content; + }, + set: function content(value) { + somewhatImplemented('TextBlock.content'); + this._content = value; + } + }, + bidiLevel: { + get: function bidiLevel() { + return this._bidiLevel; + }, + set: function bidiLevel(value) { + somewhatImplemented('TextBlock.bidiLevel'); + this._bidiLevel = value; + } + }, + firstInvalidLine: { + get: function firstInvalidLine() { + notImplemented('TextBlock.firstInvalidLine'); + return this._firstInvalidLine; + } + }, + firstLine: { + get: function firstLine() { + somewhatImplemented('TextBlock.firstLine'); + return this._firstLine; + } + }, + lastLine: { + get: function lastLine() { + somewhatImplemented('TextBlock.lastLine'); + return this._lastLine; + } + }, + textLineCreationResult: { + get: function textLineCreationResult() { + notImplemented('TextBlock.textLineCreationResult'); + return this._textLineCreationResult; + } + }, + lineRotation: { + get: function lineRotation() { + return this._lineRotation; + }, + set: function lineRotation(value) { + somewhatImplemented('TextBlock.lineRotation'); + this._lineRotation = value; + } + } + } + } + } + }; + }.call(this); +var TextElementDefinition = function () { + return { + __class__: 'flash.text.engine.TextElement', + initialize: function () { + }, + __glue__: { + native: { + static: {}, + instance: { + replaceText: function replaceText(beginIndex, endIndex, newText) { + somewhatImplemented('TextElement.replaceText'); + var text = this._text || ''; + this._text = text.slice(0, beginIndex) + newText + text.slice(endIndex); + }, + text: { + set: function text(value) { + somewhatImplemented('TextElement.text'); + this._text = value; + } + } + } + } + } + }; + }.call(this); +var TextJustifierDefinition = function () { + return { + __class__: 'flash.text.engine.TextJustifier', + initialize: function () { + this._locale = null; + this._lineJustification = null; + }, + __glue__: { + native: { + static: {}, + instance: { + setLocale: function setLocale(value) { + somewhatImplemented('TextJustifier.setLocale'); + this._locale = value; + }, + locale: { + get: function locale() { + return this._locale; + } + }, + lineJustification: { + get: function lineJustification() { + return this._lineJustification; + }, + set: function lineJustification(value) { + somewhatImplemented('TextJustifier.lineJustification'); + this._lineJustification = value; + } + } + } + } + } + }; + }.call(this); +var TextLineDefinition = function () { + return { + __class__: 'flash.text.engine.TextLine', + initialize: function () { + }, + __glue__: { + native: { + static: {}, + instance: { + getAtomIndexAtPoint: function getAtomIndexAtPoint(stageX, stageY) { + notImplemented('TextLine.getAtomIndexAtPoint'); + }, + getAtomIndexAtCharIndex: function getAtomIndexAtCharIndex(charIndex) { + notImplemented('TextLine.getAtomIndexAtCharIndex'); + }, + getAtomBounds: function getAtomBounds(atomIndex) { + notImplemented('TextLine.getAtomBounds'); + }, + getAtomBidiLevel: function getAtomBidiLevel(atomIndex) { + notImplemented('TextLine.getAtomBidiLevel'); + }, + getAtomTextRotation: function getAtomTextRotation(atomIndex) { + notImplemented('TextLine.getAtomTextRotation'); + }, + getAtomTextBlockBeginIndex: function getAtomTextBlockBeginIndex(atomIndex) { + notImplemented('TextLine.getAtomTextBlockBeginIndex'); + }, + getAtomTextBlockEndIndex: function getAtomTextBlockEndIndex(atomIndex) { + notImplemented('TextLine.getAtomTextBlockEndIndex'); + }, + getAtomCenter: function getAtomCenter(atomIndex) { + notImplemented('TextLine.getAtomCenter'); + }, + getAtomWordBoundaryOnLeft: function getAtomWordBoundaryOnLeft(atomIndex) { + notImplemented('TextLine.getAtomWordBoundaryOnLeft'); + }, + getAtomGraphic: function getAtomGraphic(atomIndex) { + notImplemented('TextLine.getAtomGraphic'); + }, + getBaselinePosition: function getBaselinePosition(baseline) { + notImplemented('TextLine.getBaselinePosition'); + }, + dump: function dump() { + notImplemented('TextLine.dump'); + }, + textBlock: { + get: function textBlock() { + notImplemented('TextLine.textBlock'); + return this._textBlock; + } + }, + hasGraphicElement: { + get: function hasGraphicElement() { + notImplemented('TextLine.hasGraphicElement'); + return this._hasGraphicElement; + } + }, + hasTabs: { + get: function hasTabs() { + notImplemented('TextLine.hasTabs'); + return this._hasTabs; + } + }, + nextLine: { + get: function nextLine() { + somewhatImplemented('TextLine.nextLine'); + return this._nextLine; + } + }, + previousLine: { + get: function previousLine() { + somewhatImplemented('TextLine.previousLine'); + return this._previousLine; + } + }, + ascent: { + get: function ascent() { + somewhatImplemented('TextLine.ascent'); + return this._ascent; + } + }, + descent: { + get: function descent() { + somewhatImplemented('TextLine.descent'); + return this._descent; + } + }, + textHeight: { + get: function textHeight() { + somewhatImplemented('TextLine.textHeight'); + return this._textHeight; + } + }, + textWidth: { + get: function textWidth() { + somewhatImplemented('TextLine.textWidth'); + return this._textWidth; + } + }, + totalAscent: { + get: function totalAscent() { + notImplemented('TextLine.totalAscent'); + return this._totalAscent; + } + }, + totalDescent: { + get: function totalDescent() { + notImplemented('TextLine.totalDescent'); + return this._totalDescent; + } + }, + totalHeight: { + get: function totalHeight() { + notImplemented('TextLine.totalHeight'); + return this._totalHeight; + } + }, + textBlockBeginIndex: { + get: function textBlockBeginIndex() { + notImplemented('TextLine.textBlockBeginIndex'); + return this._textBlockBeginIndex; + } + }, + rawTextLength: { + get: function rawTextLength() { + somewhatImplemented('TextLine.rawTextLength'); + return this._rawTextLength; + } + }, + specifiedWidth: { + get: function specifiedWidth() { + somewhatImplemented('TextLine.specifiedWidth'); + return this._specifiedWidth; + } + }, + unjustifiedTextWidth: { + get: function unjustifiedTextWidth() { + somewhatImplemented('TextLine.unjustifiedTextWidth'); + return this._unjustifiedTextWidth; + } + }, + validity: { + get: function validity() { + return this._validity; + }, + set: function validity(value) { + somewhatImplemented('TextLine.validity'); + this._validity = value; + } + }, + atomCount: { + get: function atomCount() { + notImplemented('TextLine.atomCount'); + return this._atomCount; + } + }, + mirrorRegions: { + get: function mirrorRegions() { + notImplemented('TextLine.mirrorRegions'); + return this._mirrorRegions; + } + } + } + } + } + }; + }.call(this); { var ContextMenuDefinition = function () { return { @@ -50790,6 +50450,7 @@ var ContextMenuItemDefinition = function () { var ShumwayKeyboardListener = { _lastKeyCode: 0, _captureKeyPress: false, + _charCodeMap: [], focus: null, handleEvent: function (domEvt) { var keyCode = domEvt.keyCode; @@ -50799,15 +50460,17 @@ var ShumwayKeyboardListener = { if (this._captureKeyPress) { return; } + this._charCodeMap[keyCode] = 0; } else if (domEvt.type === 'keypress') { if (this._captureKeyPress) { keyCode = this._lastKeyCode; + this._charCodeMap[keyCode] = domEvt.charCode; } else { return; } } if (this.focus) { - this.focus._dispatchEvent(new flash.events.KeyboardEvent(domEvt.type === 'keyup' ? 'keyUp' : 'keyDown', true, false, domEvt.charCode, domEvt.type === 'keyup' ? domEvt.keyCode : this._lastKeyCode, domEvt.keyLocation, domEvt.ctrlKey, domEvt.altKey, domEvt.shiftKey)); + this.focus._dispatchEvent(new flash.events.KeyboardEvent(domEvt.type === 'keyup' ? 'keyUp' : 'keyDown', true, false, domEvt.type === 'keyup' ? this._charCodeMap[keyCode] : domEvt.charCode, domEvt.type === 'keyup' ? domEvt.keyCode : this._lastKeyCode, domEvt.keyLocation, domEvt.ctrlKey, domEvt.altKey, domEvt.shiftKey)); } } }; @@ -51285,9 +50948,14 @@ var AS2GlobalsDefinition = function () { var def = { __class__: 'avm1lib.AS2Globals', initialize: function () { + flash.text.TextFormat.prototype.asDefinePublicProperty('getTextExtent', { + value: TextFormatDefinition.as2GetTextExtent, + writable: false, + enumerable: false, + configurable: false + }); } }; - var desc = Object.getOwnPropertyDescriptor; def.__glue__ = { native: { instance: { @@ -51338,15 +51006,18 @@ var AS2MovieClipDefinition = function () { return this.$nativeObject._duplicate(name, depth, initObject); }, _constructSymbol: function constructSymbol(symbolId, name) { - var theClass = AS2Context.instance.classes && AS2Context.instance.classes[name]; - if (!theClass) { - return; - } - var mc = new flash.display.MovieClip(); + var theClass = AS2Context.instance.classes && AS2Context.instance.classes[symbolId]; + var symbolProps = AS2Context.instance.assets[symbolId]; + var symbolClass = flash.display.MovieClip.class; + var mc = symbolClass.createAsSymbol(symbolProps); mc._avm1SymbolClass = theClass; + symbolClass.instanceConstructor.call(mc); this.$nativeObject.addChild(mc); return mc; }, + _gotoLabel: function (label) { + this.$nativeObject.gotoLabel(label); + }, _callFrame: function callFrame(frame) { this.$nativeObject._callFrame(frame); }, @@ -51374,7 +51045,8 @@ var AS2MovieClipDefinition = function () { _insertChildAtDepth: def._insertChildAtDepth, _duplicate: def._duplicate, _constructSymbol: def._constructSymbol, - _callFrame: def._callFrame + _callFrame: def._callFrame, + _gotoLabel: def._gotoLabel } }, script: { @@ -51414,12 +51086,54 @@ var AS2TextFieldDefinition = function () { var def = { __class__: 'avm1lib.AS2TextField', initialize: function () { + this._variable = ''; } }; - var desc = Object.getOwnPropertyDescriptor; def.__glue__ = { native: { instance: { + variable: { + get: function () { + return this._variable; + }, + set: function (name) { + if (name === this._variable) { + return; + } + this._variable = name; + var instance = this.$nativeObject; + var hasPath = name.indexOf('.') >= 0 || name.indexOf(':') >= 0; + var clip; + if (hasPath) { + var targetPath = name.split(/[.:\/]/g); + name = targetPath.pop(); + if (targetPath[0] == '_root' || targetPath[0] === '') { + clip = instance.root._getAS2Object(); + targetPath.shift(); + if (targetPath[0] === '') { + targetPath.shift(); + } + } else { + clip = instance._parent._getAS2Object(); + } + while (targetPath.length > 0) { + var childName = targetPath.shift(); + clip = clip.asGetPublicProperty(childName) || clip[childName]; + if (!clip) { + throw new Error('Cannot find ' + childName + ' variable'); + } + } + } else { + clip = instance._parent._getAS2Object(); + } + if (!clip.asHasProperty(undefined, name, 0)) { + clip.asSetPublicProperty(name, instance.text); + } + instance._addEventListener('advanceFrame', function () { + instance.text = '' + clip.asGetPublicProperty(name); + }); + } + }, _as3Object: { get: function () { return this.$nativeObject; @@ -51476,12 +51190,11 @@ var AS2UtilsDefinition = function () { configurable: false }); } - var desc = Object.getOwnPropertyDescriptor; def.__glue__ = { native: { static: { getAS2Object: function (nativeObject) { - return nativeObject ? nativeObject._getAS2Object() : null; + return nativeObject && nativeObject._getAS2Object ? nativeObject._getAS2Object() : null; }, addProperty: function (obj, propertyName, getter, setter) { obj.asDefinePublicProperty(propertyName, { @@ -51515,12 +51228,12 @@ function initDefaultListeners(thisArg) { } for (var i = 0; i < defaultListeners.length; i++) { var p = defaultListeners[i]; - p.setter.call(thisArg, p.value); + p.asGetPublicProperty('setter').call(thisArg, p.value); } } function bindNativeClassDefinition(nativeName, definition) { - natives[nativeName] = function (runtime, scope, instanceConstructor, baseClass) { - var c = new Class(undefined, instanceConstructor, Domain.coerceCallable); + natives[nativeName] = function (domain, scope, instanceConstructor, baseClass) { + var c = new Class(undefined, instanceConstructor, ApplicationDomain.coerceCallable); c.extend(baseClass); c.linkNatives(definition); return c; @@ -51616,6 +51329,8 @@ var Stubs = new function () { M('flash.display.FrameLabel', 'FrameLabelClass', FrameLabelDefinition), M('flash.display.Scene'), M('flash.display.BlendMode'), + M('flash.display.Shader', 'ShaderClass', ShaderDefinition), + M('flash.display.ShaderData', 'ShaderDataClass', ShaderDataDefinition), M('flash.filters.BevelFilter', 'BevelFilterClass', BevelFilterDefinition), M('flash.filters.BitmapFilter', 'BitmapFilterClass', BitmapFilterDefinition), M('flash.filters.BlurFilter', 'BlurFilterClass', BlurFilterDefinition), @@ -51652,15 +51367,26 @@ var Stubs = new function () { M('flash.text.Font', 'FontClass', FontDefinition), M('flash.text.TextField', 'TextFieldClass', TextFieldDefinition), M('flash.text.StaticText', 'StaticTextClass', StaticTextDefinition), + M('flash.text.StyleSheet', 'StyleSheetClass', StyleSheetDefinition), M('flash.text.TextFormat', 'TextFormatClass', TextFormatDefinition), M('flash.text.TextLineMetrics'), + M('flash.text.engine.ContentElement', 'ContentElementClass', ContentElementDefinition), + M('flash.text.engine.ElementFormat', 'ElementFormatClass', ElementFormatDefinition), M('flash.text.engine.FontDescription', 'FontDescriptionClass', FontDescriptionDefinition), + M('flash.text.engine.GroupElement', 'GroupElementClass', GroupElementDefinition), + M('flash.text.engine.SpaceJustifier', 'SpaceJustifierClass', SpaceJustifierDefinition), + M('flash.text.engine.TextBlock', 'TextBlockClass', TextBlockDefinition), + M('flash.text.engine.TextElement', 'TextElementClass', TextElementDefinition), + M('flash.text.engine.TextJustifier', 'TextJustifierClass', TextJustifierDefinition), + M('flash.text.engine.TextLine', 'TextLineClass', TextLineDefinition), M('flash.media.Sound', 'SoundClass', SoundDefinition), M('flash.media.SoundChannel', 'SoundChannelClass', SoundChannelDefinition), M('flash.media.SoundMixer', 'SoundMixerClass', SoundMixerDefinition), M('flash.media.SoundTransform', 'SoundTransformClass', SoundTransformDefinition), M('flash.media.Video', 'VideoClass', VideoDefinition), M('flash.media.ID3Info', 'ID3InfoClass', ID3InfoDefinition), + M('flash.media.Microphone', 'MicrophoneClass', MicrophoneDefinition), + M('flash.net.FileFilter', 'FileFilterClass', FileFilterDefinition), M('flash.net.NetConnection', 'NetConnectionClass', NetConnectionDefinition), M('flash.net.NetStream', 'NetStreamClass', NetStreamDefinition), M('flash.net.Responder', 'ResponderClass', ResponderDefinition), @@ -51670,6 +51396,7 @@ var Stubs = new function () { M('flash.net.SharedObject', 'SharedObjectClass', SharedObjectDefinition), M('flash.net.ObjectEncoding', 'ObjectEncodingClass', ObjectEncodingDefinition), M('flash.net.LocalConnection', 'LocalConnectionClass', LocalConnectionDefinition), + M('flash.net.Socket', 'SocketClass', SocketDefinition), M('flash.net.URLVariables'), M('packageInternal flash.system.FSCommand', 'FSCommandClass', FSCommandDefinition), M('flash.system.Capabilities', 'CapabilitiesClass', CapabilitiesDefinition), @@ -51708,25 +51435,25 @@ var Stubs = new function () { definitions[className.getOriginalName()] = m.definition; }); }(); -natives['FlashUtilScript::getAliasName'] = function (runtime, scope, instanceConstructor, baseClass) { +natives['FlashUtilScript::getAliasName'] = function (domain, scope, instanceConstructor, baseClass) { return function getAliasName(value) { return value.debugName; }; }; natives['FlashUtilScript::getDefinitionByName'] = natives.getDefinitionByName; -natives['FlashUtilScript::getTimer'] = function GetTimerMethod(runtime, scope, instanceConstructor, baseClass) { +natives['FlashUtilScript::getTimer'] = function GetTimerMethod(domain, scope, instanceConstructor, baseClass) { var start = Date.now(); return function getTimer() { return Date.now() - start; }; }; -natives['FlashUtilScript::escapeMultiByte'] = function EscapeMultiByteMethod(runtime, scope, instanceConstructor, baseClass) { +natives['FlashUtilScript::escapeMultiByte'] = function EscapeMultiByteMethod(domain, scope, instanceConstructor, baseClass) { return escape; }; -natives['FlashUtilScript::unescapeMultiByte'] = function UnescapeMultiByteMethod(runtime, scope, instanceConstructor, baseClass) { +natives['FlashUtilScript::unescapeMultiByte'] = function UnescapeMultiByteMethod(domain, scope, instanceConstructor, baseClass) { return unescape; }; -natives['FlashNetScript::navigateToURL'] = function GetNavigateToURLMethod(runtime, scope, instanceConstructor, baseClass) { +natives['FlashNetScript::navigateToURL'] = function GetNavigateToURLMethod(domain, scope, instanceConstructor, baseClass) { return function navigateToURL(request, window_) { if (request === null || request === undefined) { throwError('TypeError', Errors.NullPointerError, 'request'); @@ -51744,7 +51471,7 @@ natives['FlashNetScript::navigateToURL'] = function GetNavigateToURLMethod(runti window.open(FileLoadingService.resolveUrl(url), window_); }; }; -natives['FlashNetScript::sendToURL'] = function GetSendToURLMethod(runtime, scope, instanceConstructor, baseClass) { +natives['FlashNetScript::sendToURL'] = function GetSendToURLMethod(domain, scope, instanceConstructor, baseClass) { return function sendToURL(request) { if (request === null || request === undefined) { throwError('TypeError', Errors.NullPointerError, 'request'); @@ -51759,7 +51486,7 @@ natives['FlashNetScript::sendToURL'] = function GetSendToURLMethod(runtime, scop session.open(request); }; }; -natives['Toplevel::registerClassAlias'] = function GetRegisterClassAliasMethod(runtime, scope, instance, baseClass) { +natives['Toplevel::registerClassAlias'] = function GetRegisterClassAliasMethod(domain, scope, instance, baseClass) { return function registerClassAlias(aliasName, classObject) { if (!aliasName) { throwError('TypeError', Errors.NullPointerError, 'aliasName'); @@ -51771,7 +51498,7 @@ natives['Toplevel::registerClassAlias'] = function GetRegisterClassAliasMethod(r AMFUtils.aliasesCache.names[aliasName] = classObject; }; }; -natives['Toplevel::getClassByAlias'] = function GetGetClassByAliasMethod(runtime, scope, instance, baseClass) { +natives['Toplevel::getClassByAlias'] = function GetGetClassByAliasMethod(domain, scope, instance, baseClass) { return function getClassByAlias(aliasName) { if (!aliasName) { throwError('TypeError', Errors.NullPointerError, 'aliasName'); diff --git a/browser/extensions/shumway/content/version.txt b/browser/extensions/shumway/content/version.txt index 48c3a626cee6..dd8d5b171b1f 100644 --- a/browser/extensions/shumway/content/version.txt +++ b/browser/extensions/shumway/content/version.txt @@ -1 +1 @@ -0.7.502 +0.7.806 diff --git a/browser/extensions/shumway/content/web/avm-sandbox.js b/browser/extensions/shumway/content/web/avm-sandbox.js index 1f3590ea718f..c28492dde56e 100644 --- a/browser/extensions/shumway/content/web/avm-sandbox.js +++ b/browser/extensions/shumway/content/web/avm-sandbox.js @@ -120,6 +120,8 @@ function runViewer() { } var showURLMenu = document.getElementById('showURLMenu'); showURLMenu.addEventListener('click', showURL); + var inspectorMenu = document.getElementById('inspectorMenu'); + inspectorMenu.addEventListener('click', showInInspector); document.getElementById('copyProfileMenu').addEventListener('click', copyProfile); } @@ -129,6 +131,15 @@ function showURL() { window.prompt("Copy to clipboard", flashParams.url); } +function showInInspector() { + var base = "http://www.areweflashyet.com/shumway/examples/inspector/inspector.html?rfile="; + var params = ''; + for (var k in movieParams) { + params += '&' + k + '=' + encodeURIComponent(movieParams[k]); + } + window.open(base + encodeURIComponent(movieUrl) + params); +} + function copyProfile() { function toArray(v) { var array = []; diff --git a/browser/extensions/shumway/content/web/simple.html b/browser/extensions/shumway/content/web/simple.html index 4f868f1ba101..5dd09d4ced77 100644 --- a/browser/extensions/shumway/content/web/simple.html +++ b/browser/extensions/shumway/content/web/simple.html @@ -23,7 +23,7 @@ limitations under the License. - + diff --git a/browser/extensions/shumway/content/web/viewer.html b/browser/extensions/shumway/content/web/viewer.html index e14a3096c261..3ef3687c3322 100644 --- a/browser/extensions/shumway/content/web/viewer.html +++ b/browser/extensions/shumway/content/web/viewer.html @@ -83,6 +83,7 @@ limitations under the License. + diff --git a/browser/locales/en-US/chrome/browser/devtools/netmonitor.dtd b/browser/locales/en-US/chrome/browser/devtools/netmonitor.dtd index a056dc173fa8..4e3f1963b3ee 100644 --- a/browser/locales/en-US/chrome/browser/devtools/netmonitor.dtd +++ b/browser/locales/en-US/chrome/browser/devtools/netmonitor.dtd @@ -99,6 +99,10 @@ - in the network details footer for the "Flash" filtering button. --> + + + diff --git a/browser/metro/base/content/apzc.js b/browser/metro/base/content/apzc.js index d72d9709aa14..828e08b104a1 100644 --- a/browser/metro/base/content/apzc.js +++ b/browser/metro/base/content/apzc.js @@ -28,8 +28,7 @@ var APZCObserver = { } let os = Services.obs; - os.addObserver(this, "apzc-handle-pan-begin", false); - os.addObserver(this, "apzc-handle-pan-end", false); + os.addObserver(this, "apzc-transform-begin", false); // Fired by ContentAreaObserver window.addEventListener("SizeChanged", this, true); @@ -45,8 +44,7 @@ var APZCObserver = { } let os = Services.obs; - os.removeObserver(this, "apzc-handle-pan-begin"); - os.removeObserver(this, "apzc-handle-pan-end"); + os.removeObserver(this, "apzc-transform-begin"); window.removeEventListener("SizeChanged", this, true); @@ -86,7 +84,7 @@ var APZCObserver = { }, observe: function ao_observe(aSubject, aTopic, aData) { - if (aTopic == "apzc-handle-pan-begin") { + if (aTopic == "apzc-transform-begin") { // When we're panning, hide the main scrollbars by setting imprecise // input (which sets a property on the browser which hides the scrollbar // via CSS). This reduces jittering from left to right. We may be able diff --git a/browser/metro/base/content/bindings/browser.js b/browser/metro/base/content/bindings/browser.js index c920e306f112..8f1eb7a02759 100644 --- a/browser/metro/base/content/bindings/browser.js +++ b/browser/metro/base/content/bindings/browser.js @@ -558,7 +558,7 @@ let ContentScroll = { addMessageListener("Content:SetWindowSize", this); if (Services.prefs.getBoolPref("layers.async-pan-zoom.enabled")) { - addEventListener("scroll", this, false); + addEventListener("scroll", this, true); } addEventListener("pagehide", this, false); addEventListener("MozScrolledAreaChanged", this, false); diff --git a/browser/metro/base/content/bindings/browser.xml b/browser/metro/base/content/bindings/browser.xml index 91b124166402..89c46ea426c8 100644 --- a/browser/metro/base/content/bindings/browser.xml +++ b/browser/metro/base/content/bindings/browser.xml @@ -95,6 +95,15 @@ * @param aIgnoreScale ignore current scale factor. * @return { x: converted x coordinate, y: converted y coordinate } * + * rectClientToBrowser + * Convert a client Rect() in device pixels to page-relative + * coordinates in CSS pixels. + * + * @param aRect - client Rect to convert. + * @param aIgnoreScroll ignore root frame scroll. + * @param aIgnoreScale ignore current scale factor. + * @return converted Rect() + * * ctobx, ctoby * Convert individual x and y coordinates. * @@ -172,6 +181,39 @@ + + + + + + + + + @@ -597,6 +639,18 @@ null null + + + + + 0 0 @@ -798,9 +852,19 @@ onget="return this._contentTitle;" readonly="true"/> - + null + + + + + (viewport.x + viewport.width) || + aY > (viewport.y + viewport.height)) { + return false; + } + return true; + }, + /* * Event handlers for document events */ @@ -926,16 +932,6 @@ var SelectionHelperUI = { this._shutdown(); }, - _checkMonocleVisibility: function(aX, aY) { - if (aX < 0 || aY < 0 || - aX > ContentAreaObserver.viewableWidth || - aY > ContentAreaObserver.viewableHeight) { - this.closeEditSession(true); - return false; - } - return true; - }, - /* * Message handlers */ @@ -950,33 +946,25 @@ var SelectionHelperUI = { if (json.updateStart) { let x = this._msgTarget.btocx(json.start.xPos, true); let y = this._msgTarget.btocx(json.start.yPos, true); - if (!this._checkMonocleVisibility(x, y)) { - return; - } this.startMark.position(x, y); } + if (json.updateEnd) { let x = this._msgTarget.btocx(json.end.xPos, true); let y = this._msgTarget.btocx(json.end.yPos, true); - if (!this._checkMonocleVisibility(x, y)) { - return; - } this.endMark.position(x, y); } if (json.updateCaret) { let x = this._msgTarget.btocx(json.caret.xPos, true); let y = this._msgTarget.btocx(json.caret.yPos, true); - if (!this._checkMonocleVisibility(x, y)) { - return; - } // If selectionRangeFound is set SelectionHelper found a range we can // attach to. If not, there's no text in the control, and hence no caret // position information we can use. haveSelectionRect = json.selectionRangeFound; if (json.selectionRangeFound) { this.caretMark.position(x, y); - this.caretMark.show(); + this._showMonocles(false); } } @@ -992,7 +980,7 @@ var SelectionHelperUI = { this._targetElementRect = this._msgTarget.rectBrowserToClient(json.element, true); - // Ifd this is the end of a selection move show the appropriate + // If this is the end of a selection move show the appropriate // monocle images. src=(start, update, end, caret) if (json.src == "start" || json.src == "end") { this._showMonocles(true); @@ -1040,29 +1028,6 @@ var SelectionHelperUI = { if (this._checkForActiveDrag()) { aEvent.preventDefault(); } - let touch = aEvent.touches[0]; - this._movement.x = touch.clientX; - this._movement.y = touch.clientY; - this._movement.active = true; - break; - } - - case "touchend": - if (aEvent.touches.length == 0) - this._movement.active = false; - break; - - case "touchmove": { - if (aEvent.touches.length != 1) - break; - let touch = aEvent.touches[0]; - // Clear selection when the user pans the page - if (!this._checkForActiveDrag() && this._movement.active) { - if (Math.abs(touch.clientX - this._movement.x) > kDisableOnScrollDistance || - Math.abs(touch.clientY - this._movement.y) > kDisableOnScrollDistance) { - this.closeEditSession(true); - } - } break; } diff --git a/browser/metro/base/content/input.js b/browser/metro/base/content/input.js index 188a4d3040a5..8fb7b80d753e 100644 --- a/browser/metro/base/content/input.js +++ b/browser/metro/base/content/input.js @@ -27,13 +27,6 @@ const kMaxVelocity = 6; * prefs */ -// A debug pref that when set makes us treat all precise pointer input -// as imprecise touch input. For debugging purposes only. Note there are -// subtle event sequencing differences in this feature when running on -// the desktop using the win32 widget backend and the winrt widget backend -// in metro. Fixing something in this mode does not insure the bug is -// in metro. -const kDebugMouseInputPref = "metro.debug.treatmouseastouch"; // Display rects around selection ranges. Useful in debugging // selection problems. const kDebugSelectionDisplayPref = "metro.debug.selection.displayRanges"; @@ -104,18 +97,8 @@ var TouchModule = { Services.obs.addObserver(this, "Gesture:SingleTap", false); Services.obs.addObserver(this, "Gesture:DoubleTap", false); - - try { - this._treatMouseAsTouch = Services.prefs.getBoolPref(kDebugMouseInputPref); - } catch (e) {} }, - /* - * Mouse input source tracking - */ - - _treatMouseAsTouch: false, - /* * Events */ @@ -228,15 +211,6 @@ var TouchModule = { }, _onContextMenu: function _onContextMenu(aEvent) { - // Special case when running on the desktop, fire off - // a edge ui event when we get the contextmenu event. - if (this._treatMouseAsTouch) { - let event = document.createEvent("Events"); - event.initEvent("MozEdgeUICompleted", true, false); - window.dispatchEvent(event); - return; - } - // bug 598965 - chrome UI should stop to be pannable once the // context menu has appeared. if (ContextMenuUI.popupState) { diff --git a/browser/metro/base/tests/mochitest/browser_selection_basic.js b/browser/metro/base/tests/mochitest/browser_selection_basic.js index 90c3f5eaf513..45a90cabcb87 100644 --- a/browser/metro/base/tests/mochitest/browser_selection_basic.js +++ b/browser/metro/base/tests/mochitest/browser_selection_basic.js @@ -252,49 +252,6 @@ gTests.push({ }, }); -gTests.push({ - desc: "scroll disables", - setUp: setUpAndTearDown, - run: function test() { - sendContextMenuClick(100, 20); - - yield waitForCondition(function () { - return SelectionHelperUI.isSelectionUIVisible; - }, kCommonWaitMs, kCommonPollMs); - - is(SelectionHelperUI.isActive, true, "selection active"); - - // scroll page - sendTouchDrag(gWindow, - 400, - 400, - 400, - 350); - - yield waitForCondition(function () { - return !SelectionHelperUI.isSelectionUIVisible; - }, kCommonWaitMs, kCommonPollMs); - - // cancel fling from scroll above - TouchModule.cancelPending(); - - // active state - should be disabled after a page scroll - is(SelectionHelperUI.isActive, false, "selection inactive"); - }, - tearDown: function tearDown() { - EventUtils.synthesizeKey("VK_HOME", {}, gWindow); - emptyClipboard(); - if (gWindow) - clearSelection(gWindow); - if (gFrame) - clearSelection(gFrame); - yield waitForCondition(function () { - return !SelectionHelperUI.isSelectionUIVisible; - }, kCommonWaitMs, kCommonPollMs); - yield hideContextUI(); - }, -}); - gTests.push({ desc: "tap on selection clears selection in content", setUp: setUpAndTearDown, diff --git a/browser/metro/profile/metro.js b/browser/metro/profile/metro.js index 2e587078b153..74509b5fba69 100644 --- a/browser/metro/profile/metro.js +++ b/browser/metro/profile/metro.js @@ -26,7 +26,6 @@ pref("app.crashreporter.autosubmit", true); pref("app.crashreporter.prompted", false); // Debug prefs, see input.js -pref("metro.debug.treatmouseastouch", false); pref("metro.debug.colorizeInputOverlay", false); pref("metro.debug.selection.displayRanges", false); pref("metro.debug.selection.dumpRanges", false); diff --git a/browser/metro/theme/browser.css b/browser/metro/theme/browser.css index 2c053512c57c..dc921df8e8be 100644 --- a/browser/metro/theme/browser.css +++ b/browser/metro/theme/browser.css @@ -595,11 +595,6 @@ documenttab[selected] .documenttab-selection { padding: 0 !important; } -#urlbar-edit > hbox > .textbox-input-box > .textbox-input::-moz-selection { - color: white; - background-color: @metro_orange@; -} - #urlbar-edit > hbox > .textbox-input-box > .textbox-input:invalid { /* Hide error glow around the address bar that shows by default * when URLs are made invalid by trmming. */ diff --git a/browser/metro/theme/content.css b/browser/metro/theme/content.css index 9eeef3b2050b..07d5cce40c9e 100644 --- a/browser/metro/theme/content.css +++ b/browser/metro/theme/content.css @@ -8,10 +8,19 @@ @namespace url("http://www.w3.org/1999/xhtml"); @namespace xul url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); +/* Typography & General Styling -------------------------------------------- */ + +::-moz-selection { + color: white; + background-color: @metro_orange@; +} + *:-moz-any-link:focus { outline-offset: -2px; } +/* Input Styling -------------------------------------------- */ + select:not([size]):not([multiple]) > xul|scrollbar, select[size="1"] > xul|scrollbar, select:not([size]):not([multiple]) xul|scrollbarbutton, diff --git a/browser/metro/theme/platform.css b/browser/metro/theme/platform.css index a388959ca1ed..bb778ddc9e76 100644 --- a/browser/metro/theme/platform.css +++ b/browser/metro/theme/platform.css @@ -11,6 +11,11 @@ /* Typography & General Styling -------------------------------------------- */ +::-moz-selection { + color: white; + background-color: @metro_orange@; +} + :root { font-family: "Segoe UI", sans-serif !important; font-size: @font_normal@; diff --git a/content/base/src/nsGkAtomList.h b/content/base/src/nsGkAtomList.h index 7cc1e409f79a..5c234686e12d 100644 --- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -712,6 +712,7 @@ GK_ATOM(onerror, "onerror") GK_ATOM(onfailed, "onfailed") GK_ATOM(onfocus, "onfocus") GK_ATOM(onfrequencychange, "onfrequencychange") +GK_ATOM(onspeakerforcedchange, "onspeakerforcedchange") GK_ATOM(onget, "onget") GK_ATOM(ongroupchange, "ongroupchange") GK_ATOM(onhashchange, "onhashchange") diff --git a/content/events/public/nsEventNameList.h b/content/events/public/nsEventNameList.h index 2624672f16cf..35007245326c 100644 --- a/content/events/public/nsEventNameList.h +++ b/content/events/public/nsEventNameList.h @@ -654,6 +654,11 @@ NON_IDL_EVENT(warning, EventNameType_None, NS_EVENT) +NON_IDL_EVENT(speakerforcedchange, + NS_SPEAKERMANAGER_SPEAKERFORCEDCHANGE, + EventNameType_None, + NS_EVENT) + // Events that only have on* attributes on XUL elements NON_IDL_EVENT(text, NS_TEXT_TEXT, diff --git a/dom/apps/src/PermissionsTable.jsm b/dom/apps/src/PermissionsTable.jsm index f98b8a05804a..2c5acb717bfc 100644 --- a/dom/apps/src/PermissionsTable.jsm +++ b/dom/apps/src/PermissionsTable.jsm @@ -313,6 +313,11 @@ this.PermissionsTable = { geolocation: { privileged: DENY_ACTION, certified: ALLOW_ACTION }, + "speaker-control": { + app: DENY_ACTION, + privileged: ALLOW_ACTION, + certified: ALLOW_ACTION + }, }; /** diff --git a/dom/audiochannel/AudioChannelService.cpp b/dom/audiochannel/AudioChannelService.cpp index 361c394c4929..069e9a8cfd7f 100644 --- a/dom/audiochannel/AudioChannelService.cpp +++ b/dom/audiochannel/AudioChannelService.cpp @@ -25,6 +25,7 @@ #include "nsJSUtils.h" #include "nsCxPusher.h" #include "nsIAudioManager.h" +#include "SpeakerManagerService.h" #define NS_AUDIOMANAGER_CONTRACTID "@mozilla.org/telephony/audiomanager;1" #endif @@ -170,6 +171,12 @@ AudioChannelService::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent) UnregisterType(data->mType, data->mElementHidden, CONTENT_PROCESS_ID_MAIN, data->mWithVideo); } +#ifdef MOZ_WIDGET_GONK + bool active = AnyAudioChannelIsActive(); + for (uint32_t i = 0; i < mSpeakerManager.Length(); i++) { + mSpeakerManager[i]->SetAudioChannelActive(active); + } +#endif } void @@ -559,6 +566,19 @@ AudioChannelService::Notify(nsITimer* aTimer) return NS_OK; } +bool +AudioChannelService::AnyAudioChannelIsActive() +{ + for (int i = AUDIO_CHANNEL_INT_LAST - 1; + i >= AUDIO_CHANNEL_INT_NORMAL; --i) { + if (!mChannelCounters[i].IsEmpty()) { + return true; + } + } + + return false; +} + bool AudioChannelService::ChannelsActiveWithHigherPriorityThan( AudioChannelInternalType aType) diff --git a/dom/audiochannel/AudioChannelService.h b/dom/audiochannel/AudioChannelService.h index f68aaed403fd..fdd97099d862 100644 --- a/dom/audiochannel/AudioChannelService.h +++ b/dom/audiochannel/AudioChannelService.h @@ -18,7 +18,9 @@ namespace mozilla { namespace dom { - +#ifdef MOZ_WIDGET_GONK +class SpeakerManagerService; +#endif class AudioChannelService : public nsIObserver , public nsITimerCallback @@ -81,6 +83,21 @@ public: virtual void SetDefaultVolumeControlChannel(AudioChannelType aType, bool aHidden); + bool AnyAudioChannelIsActive(); + +#ifdef MOZ_WIDGET_GONK + void RegisterSpeakerManager(SpeakerManagerService* aSpeakerManager) + { + if (!mSpeakerManager.Contains(aSpeakerManager)) { + mSpeakerManager.AppendElement(aSpeakerManager); + } + } + + void UnregisterSpeakerManager(SpeakerManagerService* aSpeakerManager) + { + mSpeakerManager.RemoveElement(aSpeakerManager); + } +#endif protected: void Notify(); @@ -163,7 +180,9 @@ protected: AudioChannelAgentData* aData, void *aUnused); nsClassHashtable< nsPtrHashKey, AudioChannelAgentData > mAgents; - +#ifdef MOZ_WIDGET_GONK + nsTArray mSpeakerManager; +#endif nsTArray mChannelCounters[AUDIO_CHANNEL_INT_LAST]; AudioChannelType mCurrentHigherChannel; diff --git a/dom/audiochannel/AudioChannelServiceChild.cpp b/dom/audiochannel/AudioChannelServiceChild.cpp index 736cb74b8554..d2e4429c1d5f 100644 --- a/dom/audiochannel/AudioChannelServiceChild.cpp +++ b/dom/audiochannel/AudioChannelServiceChild.cpp @@ -17,6 +17,10 @@ #include "nsIObserverService.h" #include "nsThreadUtils.h" +#ifdef MOZ_WIDGET_GONK +#include "SpeakerManagerService.h" +#endif + using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::hal; @@ -120,6 +124,12 @@ AudioChannelServiceChild::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent) if (obs) { obs->NotifyObservers(nullptr, "audio-channel-agent-changed", nullptr); } +#ifdef MOZ_WIDGET_GONK + bool active = AnyAudioChannelIsActive(); + for (uint32_t i = 0; i < mSpeakerManager.Length(); i++) { + mSpeakerManager[i]->SetAudioChannelActive(active); + } +#endif } void diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 73ea860569e7..3dcf62110569 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -802,6 +802,11 @@ DOMInterfaces = { 'nativeType': 'nsDOMAttributeMap', }, +'MozSpeakerManager': { + 'nativeType': 'mozilla::dom::SpeakerManager', + 'headerFile': 'SpeakerManager.h' +}, + 'MozPowerManager': { 'nativeType': 'mozilla::dom::PowerManager', }, diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 1f7315d649dd..9f7d60141de1 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -81,6 +81,7 @@ #if defined(MOZ_WIDGET_GONK) #include "nsVolume.h" #include "nsVolumeService.h" +#include "SpeakerManagerService.h" #endif #ifdef XP_WIN @@ -586,6 +587,20 @@ ContentChild::RecvSetProcessPrivileges(const ChildPrivileges& aPrivs) return true; } +bool +ContentChild::RecvSpeakerManagerNotify() +{ +#ifdef MOZ_WIDGET_GONK + nsRefPtr service = + SpeakerManagerService::GetSpeakerManagerService(); + if (service) { + service->Notify(); + } + return true; +#endif + return false; +} + static CancelableTask* sFirstIdleTask; static void FirstIdle(void) diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index 7303b077f841..f1e16ee351f9 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -178,6 +178,8 @@ public: virtual bool RecvSetOffline(const bool& offline); + virtual bool RecvSpeakerManagerNotify(); + virtual bool RecvNotifyVisited(const URIParams& aURI); // auto remove when alertfinished is received. nsresult AddRemoteAlertObserver(const nsString& aData, nsIObserver* aObserver); diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 935ca0a979b7..c884181aa6f5 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -113,6 +113,7 @@ #ifdef MOZ_WIDGET_GONK #include "nsIVolume.h" #include "nsIVolumeService.h" +#include "SpeakerManagerService.h" using namespace mozilla::system; #endif @@ -2478,6 +2479,35 @@ ContentParent::RecvPSpeechSynthesisConstructor(PSpeechSynthesisParent* aActor) #endif } +bool +ContentParent::RecvSpeakerManagerGetSpeakerStatus(bool* aValue) +{ +#ifdef MOZ_WIDGET_GONK + *aValue = false; + nsRefPtr service = + SpeakerManagerService::GetSpeakerManagerService(); + if (service) { + *aValue = service->GetSpeakerStatus(); + } + return true; +#endif + return false; +} + +bool +ContentParent::RecvSpeakerManagerForceSpeaker(const bool& aEnable) +{ +#ifdef MOZ_WIDGET_GONK + nsRefPtr service = + SpeakerManagerService::GetSpeakerManagerService(); + if (service) { + service->ForceSpeaker(aEnable, mChildID); + } + return true; +#endif + return false; +} + bool ContentParent::RecvStartVisitedQuery(const URIParams& aURI) { @@ -2800,6 +2830,8 @@ AddGeolocationListener(nsIDOMGeoPositionCallback* watcher, bool highAccuracy) } PositionOptions* options = new PositionOptions(); + options->mTimeout = 0; + options->mMaximumAge = 0; options->mEnableHighAccuracy = highAccuracy; int32_t retval = 1; geo->WatchPosition(watcher, nullptr, options, &retval); diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index 1e6ec28e87e5..9c62df5ac8bb 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -474,6 +474,10 @@ private: virtual bool RecvBroadcastVolume(const nsString& aVolumeName); + virtual bool RecvSpeakerManagerGetSpeakerStatus(bool* aValue); + + virtual bool RecvSpeakerManagerForceSpeaker(const bool& aEnable); + virtual bool RecvSystemMessageHandled() MOZ_OVERRIDE; virtual bool RecvNuwaReady() MOZ_OVERRIDE; diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 2865dcc9f244..83a17bd79854 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -238,6 +238,8 @@ child: */ async AudioChannelNotify(); + async SpeakerManagerNotify(); + /** * Do a memory info dump to a file in our temp directory. * @@ -247,7 +249,6 @@ child: async DumpMemoryInfoToTempDir(nsString identifier, bool minimizeMemoryUsage, bool dumpChildProcesses); - /** * Dump this process's GC and CC logs. * @@ -471,6 +472,11 @@ parent: sync KeywordToURI(nsCString keyword) returns (OptionalInputStreamParams postData, OptionalURIParams uri); + sync SpeakerManagerForceSpeaker(bool aEnable); + + sync SpeakerManagerGetSpeakerStatus() + returns (bool value); + both: AsyncMessage(nsString aMessage, ClonedMessageData aData, CpowEntry[] aCpows, Principal aPrincipal); diff --git a/dom/mobilemessage/src/gonk/MobileMessageDatabaseService.js b/dom/mobilemessage/src/gonk/MobileMessageDatabaseService.js index 8067097111f7..b0d623ca347c 100644 --- a/dom/mobilemessage/src/gonk/MobileMessageDatabaseService.js +++ b/dom/mobilemessage/src/gonk/MobileMessageDatabaseService.js @@ -1781,7 +1781,10 @@ MobileMessageDatabaseService.prototype = { forEachMatchedMmsDeliveryInfo: function forEachMatchedMmsDeliveryInfo(aDeliveryInfo, aNeedle, aCallback) { - let typedAddress = MMS.Address.resolveType(aNeedle); + let typedAddress = { + type: MMS.Address.resolveType(aNeedle), + address: aNeedle + }; let normalizedAddress, parsedAddress; if (typedAddress.type === "PLMN") { normalizedAddress = PhoneNumberUtils.normalize(aNeedle, false); @@ -1789,7 +1792,10 @@ MobileMessageDatabaseService.prototype = { } for (let element of aDeliveryInfo) { - let typedStoredAddress = MMS.Address.resolveType(element.receiver); + let typedStoredAddress = { + type: MMS.Address.resolveType(element.receiver), + address: element.receiver + }; if (typedAddress.type !== typedStoredAddress.type) { // Not even my type. Skip. continue; diff --git a/dom/moz.build b/dom/moz.build index 599efe947784..53617bdf58f1 100644 --- a/dom/moz.build +++ b/dom/moz.build @@ -81,7 +81,10 @@ if CONFIG['OS_ARCH'] == 'WINNT': PARALLEL_DIRS += ['plugins/ipc/hangui'] if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk': - PARALLEL_DIRS += ['wifi'] + PARALLEL_DIRS += [ + 'speakermanager', + 'wifi', + ] if CONFIG['MOZ_B2G_RIL']: PARALLEL_DIRS += [ diff --git a/dom/speakermanager/SpeakerManager.cpp b/dom/speakermanager/SpeakerManager.cpp new file mode 100644 index 000000000000..ecfc92e24ce7 --- /dev/null +++ b/dom/speakermanager/SpeakerManager.cpp @@ -0,0 +1,219 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SpeakerManager.h" +#include "nsIDOMClassInfo.h" +#include "nsIDOMEventListener.h" +#include "SpeakerManagerService.h" +#include "nsIPermissionManager.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsIDocShell.h" +#include "nsDOMEvent.h" +#include "AudioChannelService.h" + +using namespace mozilla::dom; + +NS_IMPL_QUERY_INTERFACE_INHERITED1(SpeakerManager, nsDOMEventTargetHelper, + nsIDOMEventListener) +NS_IMPL_ADDREF_INHERITED(SpeakerManager, nsDOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(SpeakerManager, nsDOMEventTargetHelper) + +SpeakerManager::SpeakerManager() + : mForcespeaker(false) + , mVisible(false) +{ + SetIsDOMBinding(); + SpeakerManagerService *service = + SpeakerManagerService::GetSpeakerManagerService(); + if (service) { + service->RegisterSpeakerManager(this); + } +} + +SpeakerManager::~SpeakerManager() +{ + SpeakerManagerService *service = SpeakerManagerService::GetSpeakerManagerService(); + if (service) { + service->UnRegisterSpeakerManager(this); + } + nsCOMPtr target = do_QueryInterface(GetOwner()); + NS_ENSURE_TRUE_VOID(target); + + target->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"), + this, + /* useCapture = */ true); +} + +bool +SpeakerManager::Speakerforced() +{ + // If a background app calls forcespeaker=true that doesn't change anything. + // 'speakerforced' remains false everywhere. + if (mForcespeaker && !mVisible) { + return false; + } + SpeakerManagerService *service = SpeakerManagerService::GetSpeakerManagerService(); + if (service) { + return service->GetSpeakerStatus(); + } + return false; +} + +bool +SpeakerManager::Forcespeaker() +{ + return mForcespeaker; +} + +void +SpeakerManager::SetForcespeaker(bool aEnable) +{ + SpeakerManagerService *service = SpeakerManagerService::GetSpeakerManagerService(); + if (service) { + service->ForceSpeaker(aEnable, mVisible); + } + mForcespeaker = aEnable; +} + +void +SpeakerManager::DispatchSimpleEvent(const nsAString& aStr) +{ + NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); + nsresult rv = CheckInnerWindowCorrectness(); + if (NS_FAILED(rv)) { + return; + } + + nsCOMPtr event; + rv = NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to create the error event!!!"); + return; + } + rv = event->InitEvent(aStr, false, false); + + if (NS_FAILED(rv)) { + NS_WARNING("Failed to init the error event!!!"); + return; + } + + event->SetTrusted(true); + + rv = DispatchDOMEvent(nullptr, event, nullptr, nullptr); + if (NS_FAILED(rv)) { + NS_ERROR("Failed to dispatch the event!!!"); + return; + } +} + +void +SpeakerManager::Init(nsPIDOMWindow* aWindow) +{ + BindToOwner(aWindow->IsOuterWindow() ? + aWindow->GetCurrentInnerWindow() : aWindow); + + mVisible = !GetOwner()->IsBackground(); + nsCOMPtr target = do_QueryInterface(GetOwner()); + NS_ENSURE_TRUE_VOID(target); + + target->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"), + this, + /* useCapture = */ true, + /* wantsUntrusted = */ false); +} + +nsPIDOMWindow* +SpeakerManager::GetParentObject() const +{ + return GetOwner(); +} + +/* static */ already_AddRefed +SpeakerManager::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv) +{ + nsCOMPtr sgo = do_QueryInterface(aGlobal.GetAsSupports()); + if (!sgo) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsCOMPtr ownerWindow = do_QueryInterface(aGlobal.GetAsSupports()); + if (!ownerWindow) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsCOMPtr permMgr = + do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); + NS_ENSURE_TRUE(permMgr, nullptr); + + uint32_t permission = nsIPermissionManager::DENY_ACTION; + nsresult rv = + permMgr->TestPermissionFromWindow(ownerWindow, "speaker-control", + &permission); + NS_ENSURE_SUCCESS(rv, nullptr); + + if (permission != nsIPermissionManager::ALLOW_ACTION) { + aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); + return nullptr; + } + + nsRefPtr object = new SpeakerManager(); + object->Init(ownerWindow); + return object.forget(); +} + +JSObject* +SpeakerManager::WrapObject(JSContext* aCx, JS::Handle aScope) +{ + return MozSpeakerManagerBinding::Wrap(aCx, aScope, this); +} + +NS_IMETHODIMP +SpeakerManager::HandleEvent(nsIDOMEvent* aEvent) +{ + nsAutoString type; + aEvent->GetType(type); + + if (!type.EqualsLiteral("visibilitychange")) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr docshell = do_GetInterface(GetOwner()); + NS_ENSURE_TRUE(docshell, NS_ERROR_FAILURE); + docshell->GetIsActive(&mVisible); + + // If an app that has called forcespeaker=true is switched + // from the background to the foreground 'speakerforced' + // switches to true in all apps. I.e. the app doesn't have to + // call forcespeaker=true again when it comes into foreground. + SpeakerManagerService *service = + SpeakerManagerService::GetSpeakerManagerService(); + if (service && mVisible && mForcespeaker) { + service->ForceSpeaker(mForcespeaker, mVisible); + } + // If an application that has called forcespeaker=true, but no audio is + // currently playing in the app itself, if application switch to + // the background, we switch 'speakerforced' to false. + if (!mVisible && mForcespeaker) { + AudioChannelService* audioChannelService = + AudioChannelService::GetAudioChannelService(); + if (audioChannelService && !audioChannelService->AnyAudioChannelIsActive()) { + service->ForceSpeaker(false, mVisible); + } + } + return NS_OK; +} + +void +SpeakerManager::SetAudioChannelActive(bool isActive) +{ + if (!isActive && !mVisible) { + SpeakerManagerService *service = + SpeakerManagerService::GetSpeakerManagerService(); + if (service) { + service->ForceSpeaker(false, mVisible); + } + } +} diff --git a/dom/speakermanager/SpeakerManager.h b/dom/speakermanager/SpeakerManager.h new file mode 100644 index 000000000000..6838e9bd9cf2 --- /dev/null +++ b/dom/speakermanager/SpeakerManager.h @@ -0,0 +1,65 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SpeakerManager_h +#define mozilla_dom_SpeakerManager_h + +#include "nsDOMEventTargetHelper.h" +#include "mozilla/dom/MozSpeakerManagerBinding.h" + +namespace mozilla { +namespace dom { +/* This class is used for UA to control devices's speaker status. + * After UA set the speaker status, the UA should handle the + * forcespeakerchange event and change the speaker status in UI. + * The device's speaker status would set back to normal when UA close the application. + */ +class SpeakerManager MOZ_FINAL + : public nsDOMEventTargetHelper + , public nsIDOMEventListener +{ + friend class SpeakerManagerService; + friend class SpeakerManagerServiceChild; + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIDOMEVENTLISTENER + +public: + void Init(nsPIDOMWindow* aWindow); + + nsPIDOMWindow* GetParentObject() const; + + virtual JSObject* WrapObject(JSContext* aCx, + JS::Handle aScope) MOZ_OVERRIDE; + /** + * WebIDL Interface + */ + // Get this api's force speaker setting. + bool Forcespeaker(); + // Force acoustic sound go through speaker. Don't force to speaker if application + // stay in the background and re-force when application + // go to foreground + void SetForcespeaker(bool aEnable); + // Get the device's speaker forced setting. + bool Speakerforced(); + + void SetAudioChannelActive(bool aIsActive); + IMPL_EVENT_HANDLER(speakerforcedchange) + + static already_AddRefed + Constructor(const GlobalObject& aGlobal, ErrorResult& aRv); + +protected: + SpeakerManager(); + ~SpeakerManager(); + void DispatchSimpleEvent(const nsAString& aStr); + // This api's force speaker setting + bool mForcespeaker; + bool mVisible; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SpeakerManager_h diff --git a/dom/speakermanager/SpeakerManagerService.cpp b/dom/speakermanager/SpeakerManagerService.cpp new file mode 100644 index 000000000000..c3101592a71a --- /dev/null +++ b/dom/speakermanager/SpeakerManagerService.cpp @@ -0,0 +1,199 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SpeakerManagerService.h" +#include "SpeakerManagerServiceChild.h" +#include "mozilla/Services.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/unused.h" +#include "mozilla/Util.h" +#include "mozilla/dom/ContentParent.h" +#include "nsIPropertyBag2.h" +#include "nsThreadUtils.h" +#include "nsServiceManagerUtils.h" +#include "AudioChannelService.h" +#include + +#define NS_AUDIOMANAGER_CONTRACTID "@mozilla.org/telephony/audiomanager;1" +#include "nsIAudioManager.h" + +using namespace mozilla; +using namespace mozilla::dom; + +StaticRefPtr gSpeakerManagerService; + +// static +SpeakerManagerService* +SpeakerManagerService::GetSpeakerManagerService() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (XRE_GetProcessType() != GeckoProcessType_Default) { + return SpeakerManagerServiceChild::GetSpeakerManagerService(); + } + + // If we already exist, exit early + if (gSpeakerManagerService) { + return gSpeakerManagerService; + } + + // Create new instance, register, return + nsRefPtr service = new SpeakerManagerService(); + NS_ENSURE_TRUE(service, nullptr); + + gSpeakerManagerService = service; + return gSpeakerManagerService; +} + +void +SpeakerManagerService::Shutdown() +{ + if (XRE_GetProcessType() != GeckoProcessType_Default) { + return SpeakerManagerServiceChild::Shutdown(); + } + + if (gSpeakerManagerService) { + gSpeakerManagerService = nullptr; + } +} + +NS_IMPL_ISUPPORTS1(SpeakerManagerService, nsIObserver) + +void +SpeakerManagerService::ForceSpeaker(bool aEnable, uint64_t aChildId) +{ + TuruOnSpeaker(aEnable); + if (aEnable) { + mSpeakerStatusSet.Put(aChildId); + } + Notify(); + return; +} + +void +SpeakerManagerService::ForceSpeaker(bool aEnable, bool aVisible) +{ + // b2g main process without oop + TuruOnSpeaker(aEnable && aVisible); + mVisible = aVisible; + mOrgSpeakerStatus = aEnable; + Notify(); +} + +void +SpeakerManagerService::TuruOnSpeaker(bool aOn) +{ + nsCOMPtr audioManager = do_GetService(NS_AUDIOMANAGER_CONTRACTID); + NS_ENSURE_TRUE_VOID(audioManager); + int32_t phoneState; + audioManager->GetPhoneState(&phoneState); + int32_t forceuse = (phoneState == nsIAudioManager::PHONE_STATE_IN_CALL || + phoneState == nsIAudioManager::PHONE_STATE_IN_COMMUNICATION) + ? nsIAudioManager::USE_COMMUNICATION : nsIAudioManager::USE_MEDIA; + if (aOn) { + audioManager->SetForceForUse(forceuse, nsIAudioManager::FORCE_SPEAKER); + } else { + audioManager->SetForceForUse(forceuse, nsIAudioManager::FORCE_NONE); + } +} + +bool +SpeakerManagerService::GetSpeakerStatus() +{ + char propQemu[PROPERTY_VALUE_MAX]; + property_get("ro.kernel.qemu", propQemu, ""); + if (!strncmp(propQemu, "1", 1)) { + return mOrgSpeakerStatus; + } + nsCOMPtr audioManager = do_GetService(NS_AUDIOMANAGER_CONTRACTID); + NS_ENSURE_TRUE(audioManager, false); + int32_t usage; + audioManager->GetForceForUse(nsIAudioManager::USE_MEDIA, &usage); + return usage == nsIAudioManager::FORCE_SPEAKER; +} + +void +SpeakerManagerService::Notify() +{ + // Parent Notify to all the child processes. + nsTArray children; + ContentParent::GetAll(children); + for (uint32_t i = 0; i < children.Length(); i++) { + unused << children[i]->SendSpeakerManagerNotify(); + } + + for (uint32_t i = 0; i < mRegisteredSpeakerManagers.Length(); i++) { + mRegisteredSpeakerManagers[i]-> + DispatchSimpleEvent(NS_LITERAL_STRING("speakerforcedchange")); + } +} + +void +SpeakerManagerService::SetAudioChannelActive(bool aIsActive) +{ + if (!aIsActive && !mVisible) { + ForceSpeaker(!mOrgSpeakerStatus, mVisible); + } +} + +NS_IMETHODIMP +SpeakerManagerService::Observe(nsISupports* aSubject, const char* + aTopic, const PRUnichar* aData) +{ + if (!strcmp(aTopic, "ipc:content-shutdown")) { + nsCOMPtr props = do_QueryInterface(aSubject); + if (!props) { + NS_WARNING("ipc:content-shutdown message without property bag as subject"); + return NS_OK; + } + + uint64_t childID = 0; + nsresult rv = props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), + &childID); + if (NS_SUCCEEDED(rv)) { + // If the audio has paused by audiochannel, + // the enable flag should be false and don't need to handle. + if (mSpeakerStatusSet.Contains(childID)) { + TuruOnSpeaker(false); + mSpeakerStatusSet.Remove(childID); + } + if (mOrgSpeakerStatus) { + TuruOnSpeaker(!mOrgSpeakerStatus); + mOrgSpeakerStatus = false; + } + } else { + NS_WARNING("ipc:content-shutdown message without childID property"); + } + } + return NS_OK; +} + +SpeakerManagerService::SpeakerManagerService() + : mOrgSpeakerStatus(false), + mVisible(false) +{ + MOZ_COUNT_CTOR(SpeakerManagerService); + if (XRE_GetProcessType() == GeckoProcessType_Default) { + nsCOMPtr obs = mozilla::services::GetObserverService(); + if (obs) { + obs->AddObserver(this, "ipc:content-shutdown", false); + } + } + AudioChannelService* audioChannelService = + AudioChannelService::GetAudioChannelService(); + if (audioChannelService) { + audioChannelService->RegisterSpeakerManager(this); + } +} + +SpeakerManagerService::~SpeakerManagerService() +{ + MOZ_COUNT_DTOR(SpeakerManagerService); + AudioChannelService* audioChannelService = + AudioChannelService::GetAudioChannelService(); + if (audioChannelService) + audioChannelService->UnregisterSpeakerManager(this); +} diff --git a/dom/speakermanager/SpeakerManagerService.h b/dom/speakermanager/SpeakerManagerService.h new file mode 100644 index 000000000000..cc324ec897e2 --- /dev/null +++ b/dom/speakermanager/SpeakerManagerService.h @@ -0,0 +1,72 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SpeakerManagerService_h__ +#define mozilla_dom_SpeakerManagerService_h__ + +#include "nsAutoPtr.h" +#include "nsIObserver.h" +#include "nsTArray.h" +#include "SpeakerManager.h" +#include "nsIAudioManager.h" +#include "nsCheapSets.h" +#include "nsHashKeys.h" + +namespace mozilla { +namespace dom { + +class SpeakerManagerService : public nsIObserver +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + + static SpeakerManagerService* GetSpeakerManagerService(); + virtual void ForceSpeaker(bool aEnable, bool aVisible); + virtual bool GetSpeakerStatus(); + virtual void SetAudioChannelActive(bool aIsActive); + // Called by child + void ForceSpeaker(bool enable, uint64_t aChildid); + // Register the SpeakerManager to service for notify the speakerforcedchange event + void RegisterSpeakerManager(SpeakerManager* aSpeakerManager) + { + mRegisteredSpeakerManagers.AppendElement(aSpeakerManager); + } + void UnRegisterSpeakerManager(SpeakerManager* aSpeakerManager) + { + mRegisteredSpeakerManagers.RemoveElement(aSpeakerManager); + } + /** + * Shutdown the singleton. + */ + static void Shutdown(); + +protected: + SpeakerManagerService(); + + virtual ~SpeakerManagerService(); + // Notify to UA if device speaker status changed + virtual void Notify(); + + void TuruOnSpeaker(bool aEnable); + + nsTArray > mRegisteredSpeakerManagers; + // Set for remember all the child speaker status + nsCheapSet mSpeakerStatusSet; + // The Speaker status assign by UA + bool mOrgSpeakerStatus; + + bool mVisible; + // This is needed for IPC communication between + // SpeakerManagerServiceChild and this class. + friend class ContentParent; + friend class ContentChild; +}; + +} // namespace dom +} // namespace mozilla + +#endif diff --git a/dom/speakermanager/SpeakerManagerServiceChild.cpp b/dom/speakermanager/SpeakerManagerServiceChild.cpp new file mode 100644 index 000000000000..bafb4ffd6691 --- /dev/null +++ b/dom/speakermanager/SpeakerManagerServiceChild.cpp @@ -0,0 +1,113 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SpeakerManagerServiceChild.h" +#include "mozilla/Services.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/unused.h" +#include "mozilla/Util.h" +#include "mozilla/dom/ContentChild.h" +#include "mozilla/dom/ContentParent.h" +#include "nsIObserverService.h" +#include "nsThreadUtils.h" +#include "AudioChannelService.h" +#include + +using namespace mozilla; +using namespace mozilla::dom; + +StaticRefPtr gSpeakerManagerServiceChild; + +// static +SpeakerManagerService* +SpeakerManagerServiceChild::GetSpeakerManagerService() +{ + MOZ_ASSERT(NS_IsMainThread()); + + // If we already exist, exit early + if (gSpeakerManagerServiceChild) { + return gSpeakerManagerServiceChild; + } + + // Create new instance, register, return + nsRefPtr service = new SpeakerManagerServiceChild(); + NS_ENSURE_TRUE(service, nullptr); + + gSpeakerManagerServiceChild = service; + return gSpeakerManagerServiceChild; +} + +void +SpeakerManagerServiceChild::ForceSpeaker(bool aEnable, bool aVisible) +{ + mVisible = aVisible; + mOrgSpeakerStatus = aEnable; + ContentChild *cc = ContentChild::GetSingleton(); + if (cc) { + cc->SendSpeakerManagerForceSpeaker(aEnable && aVisible); + } +} + +bool +SpeakerManagerServiceChild::GetSpeakerStatus() +{ + ContentChild *cc = ContentChild::GetSingleton(); + bool status = false; + if (cc) { + cc->SendSpeakerManagerGetSpeakerStatus(&status); + } + char propQemu[PROPERTY_VALUE_MAX]; + property_get("ro.kernel.qemu", propQemu, ""); + if (!strncmp(propQemu, "1", 1)) { + return mOrgSpeakerStatus; + } + return status; +} + +void +SpeakerManagerServiceChild::Shutdown() +{ + if (gSpeakerManagerServiceChild) { + gSpeakerManagerServiceChild = nullptr; + } +} + +void +SpeakerManagerServiceChild::SetAudioChannelActive(bool aIsActive) +{ + // Content process and switch to background with no audio and speaker forced. + // Then disable speaker + for (uint32_t i = 0; i < mRegisteredSpeakerManagers.Length(); i++) { + mRegisteredSpeakerManagers[i]->SetAudioChannelActive(aIsActive); + } +} + +SpeakerManagerServiceChild::SpeakerManagerServiceChild() +{ + MOZ_ASSERT(NS_IsMainThread()); + AudioChannelService* audioChannelService = AudioChannelService::GetAudioChannelService(); + if (audioChannelService) { + audioChannelService->RegisterSpeakerManager(this); + } + MOZ_COUNT_CTOR(SpeakerManagerServiceChild); +} + +SpeakerManagerServiceChild::~SpeakerManagerServiceChild() +{ + AudioChannelService* audioChannelService = AudioChannelService::GetAudioChannelService(); + if (audioChannelService) { + audioChannelService->UnregisterSpeakerManager(this); + } + MOZ_COUNT_DTOR(SpeakerManagerServiceChild); +} + +void +SpeakerManagerServiceChild::Notify() +{ + for (uint32_t i = 0; i < mRegisteredSpeakerManagers.Length(); i++) { + mRegisteredSpeakerManagers[i]->DispatchSimpleEvent(NS_LITERAL_STRING("speakerforcedchange")); + } +} diff --git a/dom/speakermanager/SpeakerManagerServiceChild.h b/dom/speakermanager/SpeakerManagerServiceChild.h new file mode 100644 index 000000000000..28680696fe00 --- /dev/null +++ b/dom/speakermanager/SpeakerManagerServiceChild.h @@ -0,0 +1,37 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SpeakerManagerServicechild_h__ +#define mozilla_dom_SpeakerManagerServicechild_h__ + +#include "nsAutoPtr.h" +#include "nsISupports.h" +#include "SpeakerManagerService.h" + +namespace mozilla { +namespace dom { +/* This class is used to do the IPC to enable/disable speaker status + Also handle the application speaker competition problem +*/ +class SpeakerManagerServiceChild : public SpeakerManagerService +{ +public: + static SpeakerManagerService* GetSpeakerManagerService(); + static void Shutdown(); + virtual void ForceSpeaker(bool aEnable, bool aVisible) MOZ_OVERRIDE; + virtual bool GetSpeakerStatus() MOZ_OVERRIDE; + virtual void SetAudioChannelActive(bool aIsActive) MOZ_OVERRIDE; + virtual void Notify() MOZ_OVERRIDE; +protected: + SpeakerManagerServiceChild(); + virtual ~SpeakerManagerServiceChild(); +}; + +} // namespace dom +} // namespace mozilla + +#endif + diff --git a/dom/speakermanager/moz.build b/dom/speakermanager/moz.build new file mode 100644 index 000000000000..6443e1e3a621 --- /dev/null +++ b/dom/speakermanager/moz.build @@ -0,0 +1,27 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +MOCHITEST_MANIFESTS += ['tests/mochitest.ini'] + +XPIDL_MODULE = 'dom_speakermanager' + +EXPORTS += [ + 'SpeakerManager.h', + 'SpeakerManagerService.h', + 'SpeakerManagerServiceChild.h', +] + +SOURCES += [ + 'SpeakerManager.cpp', + 'SpeakerManagerService.cpp', + 'SpeakerManagerServiceChild.cpp', +] + +FAIL_ON_WARNINGS = True + +include('/ipc/chromium/chromium-config.mozbuild') + +FINAL_LIBRARY = 'gklayout' diff --git a/dom/speakermanager/tests/mochitest.ini b/dom/speakermanager/tests/mochitest.ini new file mode 100644 index 000000000000..bb272adc0047 --- /dev/null +++ b/dom/speakermanager/tests/mochitest.ini @@ -0,0 +1,3 @@ +[DEFAULT] + +[test_speakermanager.html] diff --git a/dom/speakermanager/tests/test_speakermanager.html b/dom/speakermanager/tests/test_speakermanager.html new file mode 100644 index 000000000000..18cbc098fbfd --- /dev/null +++ b/dom/speakermanager/tests/test_speakermanager.html @@ -0,0 +1,53 @@ + + + + + Test MozSpeakerManager API + + + + +

+ +
+  
+
+ + diff --git a/dom/system/gonk/RadioInterfaceLayer.js b/dom/system/gonk/RadioInterfaceLayer.js index 93dad23baca7..1ba0dd3b4517 100644 --- a/dom/system/gonk/RadioInterfaceLayer.js +++ b/dom/system/gonk/RadioInterfaceLayer.js @@ -1613,12 +1613,6 @@ RadioInterface.prototype = { this.clientId, status); }, - _isRadioChanging: function _isRadioChanging() { - let state = this.rilContext.detailedRadioState; - return state == RIL.GECKO_DETAILED_RADIOSTATE_ENABLING || - state == RIL.GECKO_DETAILED_RADIOSTATE_DISABLING; - }, - _convertRadioState: function _converRadioState(state) { switch (state) { case RIL.GECKO_RADIOSTATE_OFF: @@ -1858,7 +1852,11 @@ RadioInterface.prototype = { if (DEBUG) this.debug("Don't connect data call when Wifi is connected."); return; } - if (this._isRadioChanging()) { + + let detailedRadioState = this.rilContext.detailedRadioState; + if (gRadioEnabledController.isDeactivatingDataCalls() || + detailedRadioState == RIL.GECKO_DETAILED_RADIOSTATE_ENABLING || + detailedRadioState == RIL.GECKO_DETAILED_RADIOSTATE_DISABLING) { // We're changing the radio power currently, ignore any changes. return; } @@ -2065,7 +2063,7 @@ RadioInterface.prototype = { // At this point we could send a message to content to notify the user // that storing an incoming SMS failed, most likely due to a full disk. if (DEBUG) { - this.debug("Could not store SMS " + message.id + ", error code " + rv); + this.debug("Could not store SMS, error code " + rv); } return; } @@ -2075,8 +2073,8 @@ RadioInterface.prototype = { }.bind(this); if (message.messageClass != RIL.GECKO_SMS_MESSAGE_CLASSES[RIL.PDU_DCS_MSG_CLASS_0]) { - message.id = gMobileMessageDatabaseService.saveReceivedMessage(message, - notifyReceived); + gMobileMessageDatabaseService.saveReceivedMessage(message, + notifyReceived); } else { message.id = -1; message.threadId = 0; @@ -2638,18 +2636,15 @@ RadioInterface.prototype = { }, isValidStateForSetRadioEnabled: function() { - let state = this.rilContext.radioState; - - return !this._isRadioChanging() && - (state == RIL.GECKO_RADIOSTATE_READY || - state == RIL.GECKO_RADIOSTATE_OFF); + let state = this.rilContext.detailedRadioState; + return state == RIL.GECKO_DETAILED_RADIOSTATE_ENABLED || + state == RIL.GECKO_DETAILED_RADIOSTATE_DISABLED; }, isDummyForSetRadioEnabled: function(message) { - let state = this.rilContext.radioState; - - return (state == RIL.GECKO_RADIOSTATE_READY && message.enabled) || - (state == RIL.GECKO_RADIOSTATE_OFF && !message.enabled); + let state = this.rilContext.detailedRadioState; + return (state == RIL.GECKO_DETAILED_RADIOSTATE_ENABLED && message.enabled) || + (state == RIL.GECKO_DETAILED_RADIOSTATE_DISABLED && !message.enabled); }, setRadioEnabledResponse: function(target, message, errorMsg) { diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html index 7b2418497226..a3a37709ec38 100644 --- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -374,6 +374,7 @@ var interfaceNamesInGlobalScope = "MozSmsFilter", "MozSmsMessage", "MozSmsSegmentInfo", + {name: "MozSpeakerManager", b2g: true}, {name: "MozStkCommandEvent", b2g: true, pref: "dom.icc.enabled"}, {name: "MozTimeManager", b2g: true}, {name: "MozVoicemail", b2g: true, pref: "dom.voicemail.enabled"}, diff --git a/dom/webidl/MozSpeakerManager.webidl b/dom/webidl/MozSpeakerManager.webidl new file mode 100644 index 000000000000..56ecf866f9e5 --- /dev/null +++ b/dom/webidl/MozSpeakerManager.webidl @@ -0,0 +1,18 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +/* + * Allow application can control acoustic sound output through speaker. + * Reference https://wiki.mozilla.org/WebAPI/SpeakerManager + */ +[Constructor()] +interface MozSpeakerManager : EventTarget { + /* query the speaker status */ + readonly attribute boolean speakerforced; + /* force device device's acoustic sound output through speaker */ + attribute boolean forcespeaker; + /* this event will be fired when device's speaker forced status change */ + attribute EventHandler onspeakerforcedchange; +}; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 67945b365e96..fd0308114802 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -532,6 +532,7 @@ if CONFIG['MOZ_NFC']: if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk': WEBIDL_FILES += [ + 'MozSpeakerManager.webidl', 'MozWifiConnectionInfoEvent.webidl', 'MozWifiStatusChangeEvent.webidl', ] diff --git a/gfx/layers/ipc/AsyncPanZoomController.cpp b/gfx/layers/ipc/AsyncPanZoomController.cpp index a3b37cf24394..255c61831d71 100644 --- a/gfx/layers/ipc/AsyncPanZoomController.cpp +++ b/gfx/layers/ipc/AsyncPanZoomController.cpp @@ -992,7 +992,7 @@ bool AsyncPanZoomController::DoFling(const TimeDuration& aDelta) { if (!shouldContinueFlingX && !shouldContinueFlingY) { SendAsyncScrollEvent(); RequestContentRepaint(); - mState = NOTHING; + SetState(NOTHING); return false; } @@ -1016,8 +1016,7 @@ bool AsyncPanZoomController::DoFling(const TimeDuration& aDelta) { } void AsyncPanZoomController::CancelAnimation() { - ReentrantMonitorAutoEnter lock(mMonitor); - mState = NOTHING; + SetState(NOTHING); } void AsyncPanZoomController::SetCompositorParent(CompositorParent* aCompositorParent) { @@ -1268,7 +1267,7 @@ bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSa requestAnimationFrame = true; if (aSampleTime - mAnimationStartTime >= ZOOM_TO_DURATION) { - mState = NOTHING; + SetState(NOTHING); SendAsyncScrollEvent(); RequestContentRepaint(); } @@ -1376,7 +1375,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri mY.CancelTouch(); mFrameMetrics = aLayerMetrics; - mState = NOTHING; + SetState(NOTHING); } else { // If we're not taking the aLayerMetrics wholesale we still need to pull // in some things into our local mFrameMetrics because these things are @@ -1554,14 +1553,18 @@ void AsyncPanZoomController::SetState(PanZoomState aNewState) { } if (mGeckoContentController) { - if (IsPanningState(oldState) && !IsPanningState(aNewState)) { - mGeckoContentController->HandlePanEnd(); - } else if (!IsPanningState(oldState) && IsPanningState(aNewState)) { - mGeckoContentController->HandlePanBegin(); + if (!IsTransformingState(oldState) && IsTransformingState(aNewState)) { + mGeckoContentController->NotifyTransformBegin(); + } else if (IsTransformingState(oldState) && !IsTransformingState(aNewState)) { + mGeckoContentController->NotifyTransformEnd(); } } } +bool AsyncPanZoomController::IsTransformingState(PanZoomState aState) { + return !(aState == NOTHING || aState == TOUCHING || aState == WAITING_LISTENERS); +} + bool AsyncPanZoomController::IsPanningState(PanZoomState aState) { return (aState == PANNING || aState == PANNING_LOCKED_X || aState == PANNING_LOCKED_Y); } diff --git a/gfx/layers/ipc/AsyncPanZoomController.h b/gfx/layers/ipc/AsyncPanZoomController.h index b72c543dbafc..7e49fb654e9a 100644 --- a/gfx/layers/ipc/AsyncPanZoomController.h +++ b/gfx/layers/ipc/AsyncPanZoomController.h @@ -510,6 +510,19 @@ private: prevented the default actions yet. we still need to abort animations. */ }; + /** + * Helper to set the current state. Holds the monitor before actually setting + * it and fires content controller events based on state changes. Always set + * the state using this call, do not set it directly. + */ + void SetState(PanZoomState aState); + + /** + * Internal helpers for checking general state of this apzc. + */ + bool IsTransformingState(PanZoomState aState); + bool IsPanningState(PanZoomState mState); + enum AxisLockMode { FREE, /* No locking at all */ STANDARD, /* Default axis locking mode that remains locked until pan ends*/ @@ -518,15 +531,6 @@ private: static AxisLockMode GetAxisLockMode(); - /** - * Helper to set the current state. Holds the monitor before actually setting - * it. If the monitor is already held by the current thread, it is safe to - * instead use: |mState = NEWSTATE;| - */ - void SetState(PanZoomState aState); - - bool IsPanningState(PanZoomState mState); - uint64_t mLayersId; nsRefPtr mCompositorParent; TaskThrottler mPaintThrottler; diff --git a/gfx/layers/ipc/GeckoContentController.h b/gfx/layers/ipc/GeckoContentController.h index 8db2bcc05704..4f78217defda 100644 --- a/gfx/layers/ipc/GeckoContentController.h +++ b/gfx/layers/ipc/GeckoContentController.h @@ -77,14 +77,12 @@ public: } /** - * Request any special actions be performed when panning starts + * General tranformation notices for consumers. These fire any time + * the apzc is modifying the view, including panning, zooming, and + * fling. */ - virtual void HandlePanBegin() {} - - /** - * Request any special actions be performed when panning ends - */ - virtual void HandlePanEnd() {} + virtual void NotifyTransformBegin() {} + virtual void NotifyTransformEnd() {} GeckoContentController() {} virtual ~GeckoContentController() {} diff --git a/hal/gonk/GonkFMRadio.cpp b/hal/gonk/GonkFMRadio.cpp index a9cc22da0798..8a6549df48ca 100644 --- a/hal/gonk/GonkFMRadio.cpp +++ b/hal/gonk/GonkFMRadio.cpp @@ -36,8 +36,8 @@ static int sRadioFD; static bool sRadioEnabled; static pthread_t sRadioThread; static hal::FMRadioSettings sRadioSettings; -static int sTavaruaVersion; -static bool sTavaruaMode; +static int sMsmFMVersion; +static bool sMsmFMMode; static int setControl(uint32_t id, int32_t value) @@ -69,12 +69,12 @@ public: /* Runs on the radio thread */ static void -initTavaruaRadio(hal::FMRadioSettings &aInfo) +initMsmFMRadio(hal::FMRadioSettings &aInfo) { mozilla::ScopedClose fd(sRadioFD); char version[64]; int rc; - snprintf(version, sizeof(version), "%d", sTavaruaVersion); + snprintf(version, sizeof(version), "%d", sMsmFMVersion); property_set("hw.fm.version", version); /* Set the mode for soc downloader */ @@ -199,9 +199,9 @@ initTavaruaRadio(hal::FMRadioSettings &aInfo) /* Runs on the radio thread */ static void * -runTavaruaRadio(void *) +runMsmFMRadio(void *) { - initTavaruaRadio(sRadioSettings); + initMsmFMRadio(sRadioSettings); if (!sRadioEnabled) { NS_DispatchToMainThread(new RadioUpdate(hal::FM_RADIO_OPERATION_ENABLE, hal::FM_RADIO_OPERATION_STATUS_FAIL)); @@ -275,7 +275,8 @@ EnableFMRadio(const hal::FMRadioSettings& aInfo) return; } - sTavaruaMode = !strcmp((char *)cap.driver, "radio-tavarua"); + sMsmFMMode = !strcmp((char *)cap.driver, "radio-tavarua") || + !strcmp((char *)cap.driver, "radio-iris"); HAL_LOG(("Radio: %s (%s)\n", cap.driver, cap.card)); if (!(cap.capabilities & V4L2_CAP_RADIO)) { @@ -289,10 +290,10 @@ EnableFMRadio(const hal::FMRadioSettings& aInfo) } sRadioSettings = aInfo; - if (sTavaruaMode) { + if (sMsmFMMode) { sRadioFD = fd.forget(); - sTavaruaVersion = cap.version; - pthread_create(&sRadioThread, nullptr, runTavaruaRadio, nullptr); + sMsmFMVersion = cap.version; + pthread_create(&sRadioThread, nullptr, runMsmFMRadio, nullptr); return; } @@ -323,7 +324,7 @@ DisableFMRadio() sRadioEnabled = false; - if (sTavaruaMode) { + if (sMsmFMMode) { int rc = setControl(V4L2_CID_PRIVATE_TAVARUA_STATE, FM_OFF); if (rc < 0) { HAL_LOG(("Unable to turn off radio")); @@ -355,7 +356,7 @@ FMRadioSeek(const hal::FMRadioSeekDirection& aDirection) #endif int rc = ioctl(sRadioFD, VIDIOC_S_HW_FREQ_SEEK, &seek); - if (sTavaruaMode && rc >= 0) + if (sMsmFMMode && rc >= 0) return; hal::FMRadioOperationInformation info; @@ -403,7 +404,7 @@ SetFMRadioFrequency(const uint32_t frequency) if (rc < 0) HAL_LOG(("Could not set radio frequency")); - if (sTavaruaMode && rc >= 0) + if (sMsmFMMode && rc >= 0) return; hal::FMRadioOperationInformation info; diff --git a/hal/gonk/GonkSensor.cpp b/hal/gonk/GonkSensor.cpp index 36396f99c9cb..b3a13ef93d56 100644 --- a/hal/gonk/GonkSensor.cpp +++ b/hal/gonk/GonkSensor.cpp @@ -186,6 +186,14 @@ PollSensors() if (buffer[i].type == SENSOR_TYPE_MAGNETIC_FIELD) continue; + // Bug 938035, transfer HAL data for orientation sensor to meet w3c spec + // ex: HAL report alpha=90 means East but alpha=90 means West in w3c spec + if (buffer[i].type == SENSOR_TYPE_ORIENTATION) { + buffer[i].orientation.azimuth = 360 - buffer[i].orientation.azimuth; + buffer[i].orientation.pitch = -buffer[i].orientation.pitch; + buffer[i].orientation.roll = -buffer[i].orientation.roll; + } + if (HardwareSensorToHalSensor(buffer[i].type) == SENSOR_UNKNOWN) { // Emulator is broken and gives us events without types set int index; diff --git a/layout/build/Makefile.in b/layout/build/Makefile.in index ec03df74ae4d..00978eb6d775 100644 --- a/layout/build/Makefile.in +++ b/layout/build/Makefile.in @@ -78,6 +78,7 @@ LOCAL_INCLUDES += -I$(srcdir)/../base \ -I$(topsrcdir)/dom/audiochannel \ -I$(topsrcdir)/dom/telephony \ -I$(topsrcdir)/dom/media \ + -I$(topsrcdir)/dom/speakermanager \ -I. \ -I$(topsrcdir)/editor/libeditor/base \ -I$(topsrcdir)/editor/libeditor/text \ @@ -100,6 +101,7 @@ endif ifeq (gonk,$(MOZ_WIDGET_TOOLKIT)) LOCAL_INCLUDES += -I$(topsrcdir)/dom/system/gonk +LOCAL_INCLUDES += -I$(topsrcdir)/dom/speakermanager endif #} ifdef MOZ_B2G_FM #{ diff --git a/layout/build/nsLayoutStatics.cpp b/layout/build/nsLayoutStatics.cpp index f1227c5699e0..1b11a9caaa5e 100644 --- a/layout/build/nsLayoutStatics.cpp +++ b/layout/build/nsLayoutStatics.cpp @@ -100,6 +100,7 @@ #ifdef MOZ_WIDGET_GONK #include "nsVolumeService.h" +#include "SpeakerManagerService.h" using namespace mozilla::system; #endif @@ -372,6 +373,7 @@ nsLayoutStatics::Shutdown() #ifdef MOZ_WIDGET_GONK nsVolumeService::Shutdown(); + SpeakerManagerService::Shutdown(); #endif #ifdef MOZ_WEBSPEECH diff --git a/mobile/android/base/widget/ActivityChooserModel.java b/mobile/android/base/widget/ActivityChooserModel.java index e8e94d966ffc..182ee2e35489 100644 --- a/mobile/android/base/widget/ActivityChooserModel.java +++ b/mobile/android/base/widget/ActivityChooserModel.java @@ -668,6 +668,20 @@ public class ActivityChooserModel extends DataSetObservable { } } + public int getDistinctActivityCountInHistory() { + synchronized (mInstanceLock) { + ensureConsistentState(); + final List packages = new ArrayList(); + for (HistoricalRecord record : mHistoricalRecords) { + String activity = record.activity.flattenToString(); + if (!packages.contains(activity)) { + packages.add(activity); + } + } + return packages.size(); + } + } + @Override protected void finalize() throws Throwable { super.finalize(); diff --git a/mobile/android/base/widget/GeckoActionProvider.java b/mobile/android/base/widget/GeckoActionProvider.java index 053fbe77162f..7db3cf54d5e2 100644 --- a/mobile/android/base/widget/GeckoActionProvider.java +++ b/mobile/android/base/widget/GeckoActionProvider.java @@ -54,7 +54,7 @@ public class GeckoActionProvider extends ActionProvider { view.setActionButtonClickListener(mCallbacks); final PackageManager packageManager = mContext.getPackageManager(); - int historySize = dataModel.getHistorySize(); + int historySize = dataModel.getDistinctActivityCountInHistory(); if (historySize > 2) { historySize = 2; } @@ -142,4 +142,3 @@ public class GeckoActionProvider extends ActionProvider { } } } - diff --git a/toolkit/components/osfile/modules/osfile_shared_front.jsm b/toolkit/components/osfile/modules/osfile_shared_front.jsm index f6f09dc73d0c..084ef1a776b7 100644 --- a/toolkit/components/osfile/modules/osfile_shared_front.jsm +++ b/toolkit/components/osfile/modules/osfile_shared_front.jsm @@ -474,8 +474,10 @@ AbstractFile.writeAtomic = */ AbstractFile.removeDir = function(path, options = {}) { let iterator = new OS.File.DirectoryIterator(path); - if (!iterator.exists() && options.ignoreAbsent) { - return; + if (!iterator.exists()) { + if (!("ignoreAbsent" in options) || options.ignoreAbsent) { + return; + } } try { diff --git a/toolkit/components/osfile/tests/xpcshell/test_removeDir.js b/toolkit/components/osfile/tests/xpcshell/test_removeDir.js index af1b2138b317..095b8f0d4fa0 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_removeDir.js +++ b/toolkit/components/osfile/tests/xpcshell/test_removeDir.js @@ -36,7 +36,7 @@ add_task(function() { // Remove non-existent directory let exception = null; try { - yield OS.File.removeDir(dir); + yield OS.File.removeDir(dir, {ignoreAbsent: false}); } catch (ex) { exception = ex; } @@ -46,12 +46,13 @@ add_task(function() { // Remove non-existent directory with ignoreAbsent yield OS.File.removeDir(dir, {ignoreAbsent: true}); + yield OS.File.removeDir(dir); - // Remove file + // Remove file with ignoreAbsent: false yield OS.File.writeAtomic(file, "content", { tmpPath: file + ".tmp" }); exception = null; try { - yield OS.File.removeDir(file); + yield OS.File.removeDir(file, {ignoreAbsent: false}); } catch (ex) { exception = ex; } @@ -67,15 +68,14 @@ add_task(function() { // Remove directory that contains one file yield OS.File.makeDir(dir); yield OS.File.writeAtomic(file1, "content", { tmpPath: file1 + ".tmp" }); - //yield OS.File.open(file1, {create:true}); - yield OS.File.removeDir(dir) + yield OS.File.removeDir(dir); do_check_false((yield OS.File.exists(dir))); // Remove directory that contains multiple files yield OS.File.makeDir(dir); yield OS.File.writeAtomic(file1, "content", { tmpPath: file1 + ".tmp" }); yield OS.File.writeAtomic(file2, "content", { tmpPath: file2 + ".tmp" }); - yield OS.File.removeDir(dir) + yield OS.File.removeDir(dir); do_check_false((yield OS.File.exists(dir))); // Remove directory that contains a file and a directory diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 9eb00714b1d3..88ded96fb375 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -2831,6 +2831,13 @@ "extended_statistics_ok": true, "description": "Session restore: Duration of the longest uninterruptible operation while writing session data (ms)" }, + "FX_SESSION_RESTORE_FILE_SIZE_BYTES": { + "kind": "exponential", + "high": 50000000, + "n_buckets": 30, + "extended_statistics_ok": true, + "description": "Session restore: The size of file sessionstore.js (bytes)" + }, "FX_SESSION_RESTORE_CORRUPT_FILE": { "kind": "boolean", "description": "Session restore: Whether the file read on startup contained parse-able JSON" diff --git a/toolkit/devtools/server/actors/webconsole.js b/toolkit/devtools/server/actors/webconsole.js index 1a4bc0226003..00973ef12fe7 100644 --- a/toolkit/devtools/server/actors/webconsole.js +++ b/toolkit/devtools/server/actors/webconsole.js @@ -227,6 +227,13 @@ WebConsoleActor.prototype = */ consoleReflowListener: null, + /** + * The JSTerm Helpers names cache. + * @private + * @type array + */ + _jstermHelpersCache: null, + /** * Getter for the NetworkMonitor.saveRequestAndResponseBodies preference. * @type boolean @@ -692,12 +699,29 @@ WebConsoleActor.prototype = */ onAutocomplete: function WCA_onAutocomplete(aRequest) { - // TODO: Bug 842682 - use the debugger API for autocomplete in the Web - // Console, and provide suggestions from the selected debugger stack frame. - // Also, properly reuse _getJSTermHelpers instead of re-implementing it - // here. - let result = JSPropertyProvider(this.window, aRequest.text, - aRequest.cursor) || {}; + let frameActorId = aRequest.frameActor; + let dbgObject = null; + let environment = null; + + // This is the case of the paused debugger + if (frameActorId) { + let frameActor = this.conn.getActor(frameActorId); + if (frameActor) { + let frame = frameActor.frame; + environment = frame.environment; + } + else { + Cu.reportError("Web Console Actor: the frame actor was not found: " + + frameActorId); + } + } + // This is the general case (non-paused debugger) + else { + dbgObject = this.dbg.makeGlobalObjectReference(this.window); + } + + let result = JSPropertyProvider(dbgObject, environment, aRequest.text, + aRequest.cursor, frameActorId) || {}; let matches = result.matches || []; let reqText = aRequest.text.substr(0, aRequest.cursor); @@ -705,13 +729,14 @@ WebConsoleActor.prototype = // helper functions. let lastNonAlphaIsDot = /[.][a-zA-Z0-9$]*$/.test(reqText); if (!lastNonAlphaIsDot) { - let helpers = { - sandbox: Object.create(null) - }; - JSTermHelpers(helpers); - - let helperNames = Object.getOwnPropertyNames(helpers.sandbox); - matches = matches.concat(helperNames.filter(n => n.startsWith(result.matchProp))); + if (!this._jstermHelpersCache) { + let helpers = { + sandbox: Object.create(null) + }; + JSTermHelpers(helpers); + this._jstermHelpersCache = Object.getOwnPropertyNames(helpers.sandbox); + } + matches = matches.concat(this._jstermHelpersCache.filter(n => n.startsWith(result.matchProp))); } return { diff --git a/toolkit/devtools/server/actors/webgl.js b/toolkit/devtools/server/actors/webgl.js index f5bfdb890b0a..e52b44507208 100644 --- a/toolkit/devtools/server/actors/webgl.js +++ b/toolkit/devtools/server/actors/webgl.js @@ -15,16 +15,10 @@ const { method, Arg, Option, RetVal } = protocol; const WEBGL_CONTEXT_NAMES = ["webgl", "experimental-webgl", "moz-webgl"]; -const HIGHLIGHT_FRAG_SHADER = [ - "precision lowp float;", - "void main() {", - "gl_FragColor.rgba = vec4(%color);", - "}" -].join("\n"); - // These traits are bit masks. Make sure they're powers of 2. const PROGRAM_DEFAULT_TRAITS = 0; const PROGRAM_BLACKBOX_TRAIT = 1; +const PROGRAM_HIGHLIGHT_TRAIT = 2; exports.register = function(handle) { handle.addTabActor(WebGLActor, "webglActor"); @@ -160,26 +154,21 @@ let ProgramActor = protocol.ActorClass({ }), /** - * Replaces this program's fragment shader with an temporary - * easy-to-distinguish alternative. See HIGHLIGHT_FRAG_SHADER. + * Highlights any geometry rendered using this program. */ - highlight: method(function(color) { - let shaderActor = this._getShaderActor("fragment"); - let oldText = shaderActor.text; - let newText = HIGHLIGHT_FRAG_SHADER.replace("%color", color) - shaderActor.compile(newText); - shaderActor.text = oldText; + highlight: method(function(tint) { + this.linkedProxy.highlightTint = tint; + this.linkedCache.setProgramTrait(this.program, PROGRAM_HIGHLIGHT_TRAIT); }, { - request: { color: Arg(0, "array:string") }, + request: { tint: Arg(0, "array:number") }, oneway: true }), /** - * Reverts this program's fragment shader to the latest user-defined source. + * Allows geometry to be rendered normally using this program. */ unhighlight: method(function() { - let shaderActor = this._getShaderActor("fragment"); - shaderActor.compile(shaderActor.text); + this.linkedCache.unsetProgramTrait(this.program, PROGRAM_HIGHLIGHT_TRAIT); }, { oneway: true }), @@ -477,29 +466,31 @@ let WebGLInstrumenter = { * The targeted WebGL context instance. * @param string funcName * The function to override. - * @param string callbackName [optional] - * A custom callback function name in the observer. If unspecified, - * it will default to the name of the function to override. + * @param array callbackName [optional] + * The two callback function names in the observer, corresponding to + * the "before" and "after" invocation times. If unspecified, they will + * default to the name of the function to override. * @param number timing [optional] * When to issue the callback in relation to the actual context - * function call. Availalble values are 0 for "before" (default) - * and 1 for "after". + * function call. Availalble values are -1 for "before" (default) + * 1 for "after" and 0 for "before and after". */ - _instrument: function(observer, context, funcName, callbackName, timing = 0) { + _instrument: function(observer, context, funcName, callbackName = [], timing = -1) { let { cache, proxy } = observer.for(context); let originalFunc = context[funcName]; - let proxyFuncName = callbackName || funcName; + let beforeFuncName = callbackName[0] || funcName; + let afterFuncName = callbackName[1] || callbackName[0] || funcName; context[funcName] = function(...glArgs) { - if (timing == 0 && !observer.suppressHandlers) { - let glBreak = observer[proxyFuncName](glArgs, cache, proxy); + if (timing <= 0 && !observer.suppressHandlers) { + let glBreak = observer[beforeFuncName](glArgs, cache, proxy); if (glBreak) return undefined; } let glResult = originalFunc.apply(this, glArgs); - if (timing == 1 && !observer.suppressHandlers) { - let glBreak = observer[proxyFuncName](glArgs, glResult, cache, proxy); + if (timing >= 0 && !observer.suppressHandlers) { + let glBreak = observer[afterFuncName](glArgs, glResult, cache, proxy); if (glBreak) return undefined; } @@ -516,19 +507,28 @@ let WebGLInstrumenter = { "linkProgram", "getAttribLocation", "getUniformLocation" ] }, { - callback: "toggleVertexAttribArray", + timing: -1, // before + callback: [ + "toggleVertexAttribArray" + ], functions: [ "enableVertexAttribArray", "disableVertexAttribArray" ] }, { - callback: "attribute_", + timing: -1, // before + callback: [ + "attribute_" + ], functions: [ "vertexAttrib1f", "vertexAttrib2f", "vertexAttrib3f", "vertexAttrib4f", "vertexAttrib1fv", "vertexAttrib2fv", "vertexAttrib3fv", "vertexAttrib4fv", "vertexAttribPointer" ] }, { - callback: "uniform_", + timing: -1, // before + callback: [ + "uniform_" + ], functions: [ "uniform1i", "uniform2i", "uniform3i", "uniform4i", "uniform1f", "uniform2f", "uniform3f", "uniform4f", @@ -537,10 +537,17 @@ let WebGLInstrumenter = { "uniformMatrix2fv", "uniformMatrix3fv", "uniformMatrix4fv" ] }, { - timing: 1, // after - functions: ["useProgram"] + timing: -1, // before + functions: [ + "useProgram", "enable", "disable", "blendColor", + "blendEquation", "blendEquationSeparate", + "blendFunc", "blendFuncSeparate" + ] }, { - callback: "draw_", + timing: 0, // before and after + callback: [ + "beforeDraw_", "afterDraw_" + ], functions: [ "drawArrays", "drawElements" ] @@ -573,6 +580,7 @@ WebGLObserver.prototype = { registerContextForWindow: function(id, context) { let cache = new WebGLCache(id, context); let proxy = new WebGLProxy(id, context, cache, this); + cache.refreshState(proxy); this._contexts.set(context, { ownerWindow: id, @@ -706,21 +714,116 @@ WebGLObserver.prototype = { }, /** - * Called immediately *after* 'useProgram' is requested in the context. + * Called immediately *before* 'useProgram' is requested in the context. * * @param array glArgs * Overridable arguments with which the function is called. - * @param void glResult - * The returned value of the original function call. * @param WebGLCache cache * The state storage for the WebGL context initiating this call. */ - useProgram: function(glArgs, glResult, cache) { + useProgram: function(glArgs, cache) { // Manually keeping a cache and not using gl.getParameter(CURRENT_PROGRAM) // because gl.get* functions are slow as potatoes. cache.currentProgram = glArgs[0]; }, + /** + * Called immediately *before* 'enable' is requested in the context. + * + * @param array glArgs + * Overridable arguments with which the function is called. + * @param WebGLCache cache + * The state storage for the WebGL context initiating this call. + */ + enable: function(glArgs, cache) { + cache.currentState[glArgs[0]] = true; + }, + + /** + * Called immediately *before* 'disable' is requested in the context. + * + * @param array glArgs + * Overridable arguments with which the function is called. + * @param WebGLCache cache + * The state storage for the WebGL context initiating this call. + */ + disable: function(glArgs, cache) { + cache.currentState[glArgs[0]] = false; + }, + + /** + * Called immediately *before* 'blendColor' is requested in the context. + * + * @param array glArgs + * Overridable arguments with which the function is called. + * @param WebGLCache cache + * The state storage for the WebGL context initiating this call. + */ + blendColor: function(glArgs, cache) { + let blendColor = cache.currentState.blendColor; + blendColor[0] = glArgs[0]; + blendColor[1] = glArgs[1]; + blendColor[2] = glArgs[2]; + blendColor[3] = glArgs[3]; + }, + + /** + * Called immediately *before* 'blendEquation' is requested in the context. + * + * @param array glArgs + * Overridable arguments with which the function is called. + * @param WebGLCache cache + * The state storage for the WebGL context initiating this call. + */ + blendEquation: function(glArgs, cache) { + let state = cache.currentState; + state.blendEquationRgb = state.blendEquationAlpha = glArgs[0]; + }, + + /** + * Called immediately *before* 'blendEquationSeparate' is requested in the context. + * + * @param array glArgs + * Overridable arguments with which the function is called. + * @param WebGLCache cache + * The state storage for the WebGL context initiating this call. + */ + blendEquationSeparate: function(glArgs, cache) { + let state = cache.currentState; + state.blendEquationRgb = glArgs[0]; + state.blendEquationAlpha = glArgs[1]; + }, + + /** + * Called immediately *before* 'blendFunc' is requested in the context. + * + * @param array glArgs + * Overridable arguments with which the function is called. + * @param WebGLCache cache + * The state storage for the WebGL context initiating this call. + */ + blendFunc: function(glArgs, cache) { + let state = cache.currentState; + state.blendSrcRgb = state.blendSrcAlpha = glArgs[0]; + state.blendDstRgb = state.blendDstAlpha = glArgs[1]; + }, + + /** + * Called immediately *before* 'blendFuncSeparate' is requested in the context. + * + * @param array glArgs + * Overridable arguments with which the function is called. + * @param WebGLCache cache + * The state storage for the WebGL context initiating this call. + */ + blendFuncSeparate: function(glArgs, cache) { + let state = cache.currentState; + state.blendSrcRgb = glArgs[0]; + state.blendDstRgb = glArgs[1]; + state.blendSrcAlpha = glArgs[2]; + state.blendDstAlpha = glArgs[3]; + }, + /** * Called immediately *before* 'drawArrays' or 'drawElements' is requested * in the context. @@ -729,10 +832,44 @@ WebGLObserver.prototype = { * Overridable arguments with which the function is called. * @param WebGLCache cache * The state storage for the WebGL context initiating this call. + * @param WebGLProxy proxy + * The proxy methods for the WebGL context initiating this call. */ - draw_: function(glArgs, cache) { - // Return true to break original function call. - return cache.currentProgramTraits & PROGRAM_BLACKBOX_TRAIT; + beforeDraw_: function(glArgs, cache, proxy) { + let traits = cache.currentProgramTraits; + + // Handle program blackboxing. + if (traits & PROGRAM_BLACKBOX_TRAIT) { + return true; // Return true to break original function call. + } + // Handle program highlighting. + if (traits & PROGRAM_HIGHLIGHT_TRAIT) { + proxy.enableHighlighting(); + } + + return false; + }, + + /** + * Called immediately *after* 'drawArrays' or 'drawElements' is requested + * in the context. + * + * @param array glArgs + * Overridable arguments with which the function is called. + * @param void glResult + * The returned value of the original function call. + * @param WebGLCache cache + * The state storage for the WebGL context initiating this call. + * @param WebGLProxy proxy + * The proxy methods for the WebGL context initiating this call. + */ + afterDraw_: function(glArgs, glResult, cache, proxy) { + let traits = cache.currentProgramTraits; + + // Handle program highlighting. + if (traits & PROGRAM_HIGHLIGHT_TRAIT) { + proxy.disableHighlighting(); + } } }; @@ -749,6 +886,7 @@ function WebGLCache(id, context) { this._id = id; this._gl = context; this._programs = new Map(); + this.currentState = {}; } WebGLCache.prototype = { @@ -762,6 +900,35 @@ WebGLCache.prototype = { get ownerWindow() this._id, get ownerContext() this._gl, + /** + * A collection of flags or properties representing the context's state. + * Implemented as an object hash and not a Map instance because keys are + * always either strings or numbers. + */ + currentState: null, + + /** + * Populates the current state with values retrieved from the context. + * + * @param WebGLProxy proxy + * The proxy methods for the WebGL context owning the state. + */ + refreshState: function(proxy) { + let gl = this._gl; + let s = this.currentState; + + // Populate only with the necessary parameters. Not all default WebGL + // state values are required. + s[gl.BLEND] = proxy.isEnabled("BLEND"); + s.blendColor = proxy.getParameter("BLEND_COLOR"); + s.blendEquationRgb = proxy.getParameter("BLEND_EQUATION_RGB"); + s.blendEquationAlpha = proxy.getParameter("BLEND_EQUATION_ALPHA"); + s.blendSrcRgb = proxy.getParameter("BLEND_SRC_RGB"); + s.blendSrcAlpha = proxy.getParameter("BLEND_SRC_ALPHA"); + s.blendDstRgb = proxy.getParameter("BLEND_DST_RGB"); + s.blendDstAlpha = proxy.getParameter("BLEND_DST_ALPHA"); + }, + /** * Adds a program to the cache. * @@ -947,10 +1114,14 @@ function WebGLProxy(id, context, cache, observer) { this._observer = observer; let exports = [ + "isEnabled", + "getParameter", "getAttachedShaders", "getShaderSource", "getShaderOfType", - "compileShader" + "compileShader", + "enableHighlighting", + "disableHighlighting" ]; exports.forEach(e => this[e] = (...args) => this._call(e, args)); } @@ -964,6 +1135,64 @@ WebGLProxy.prototype = { get ownerWindow() this._id, get ownerContext() this._gl, + /** + * Test whether a WebGL capability is enabled. + * + * @param string name + * The WebGL capability name, for example "BLEND". + * @return boolean + * True if enabled, false otherwise. + */ + _isEnabled: function(name) { + return this._gl.isEnabled(this._gl[name]); + }, + + /** + * Returns the value for the specified WebGL parameter name. + * + * @param string name + * The WebGL parameter name, for example "BLEND_COLOR". + * @return any + * The corresponding parameter's value. + */ + _getParameter: function(name) { + return this._gl.getParameter(this._gl[name]); + }, + + /** + * Returns the renderbuffer property value for the specified WebGL parameter. + * If no renderbuffer binding is available, null is returned. + * + * @param string name + * The WebGL parameter name, for example "BLEND_COLOR". + * @return any + * The corresponding parameter's value. + */ + _getRenderbufferParameter: function(name) { + if (!this._getParameter("RENDERBUFFER_BINDING")) { + return null; + } + let gl = this._gl; + return gl.getRenderbufferParameter(gl.RENDERBUFFER, gl[name]); + }, + + /** + * Returns the framebuffer property value for the specified WebGL parameter. + * If no framebuffer binding is available, null is returned. + * + * @param string name + * The WebGL parameter name, for example "BLEND_COLOR". + * @return any + * The corresponding parameter's value. + */ + _getFramebufferAttachmentParameter: function(type, name) { + if (!this._getParameter("FRAMEBUFFER_BINDING")) { + return null; + } + let gl = this._gl; + return gl.getFramebufferAttachmentParameter(gl.RENDERBUFFER, gl[type], gl[name]); + }, + /** * Returns the shader objects attached to a program object. * @@ -1046,6 +1275,48 @@ WebGLProxy.prototype = { return error; }, + /** + * Enables color blending based on the geometry highlight tint. + */ + _enableHighlighting: function() { + let gl = this._gl; + + // Avoid changing the blending params when rendering to a depth texture. + let format = this._getRenderbufferParameter("RENDERBUFFER_INTERNAL_FORMAT"); + if (format == gl.DEPTH_COMPONENT16) { + return; + } + + // Non-premultiplied alpha blending based on a predefined constant color. + // Simply using gl.colorMask won't work, because we want non-tinted colors + // to be drawn as black, not ignored. + gl.enable(gl.BLEND); + gl.blendColor.apply(gl, this.highlightTint); + gl.blendEquation(gl.FUNC_ADD); + gl.blendFunc(gl.CONSTANT_COLOR, gl.ONE_MINUS_SRC_ALPHA, gl.CONSTANT_COLOR, gl.ZERO); + this.wasHighlighting = true; + }, + + /** + * Disables color blending based on the geometry highlight tint, by + * reverting the corresponding params back to their original values. + */ + _disableHighlighting: function() { + let gl = this._gl; + let s = this._cache.currentState; + + gl[s[gl.BLEND] ? "enable" : "disable"](gl.BLEND); + gl.blendColor.apply(gl, s.blendColor); + gl.blendEquationSeparate(s.blendEquationRgb, s.blendEquationAlpha); + gl.blendFuncSeparate(s.blendSrcRgb, s.blendDstRgb, s.blendSrcAlpha, s.blendDstAlpha); + }, + + /** + * The color tint used for highlighting geometry. + * @see _enableHighlighting and _disableHighlighting. + */ + highlightTint: [0, 0, 0, 0], + /** * Executes a function in this object. * diff --git a/toolkit/devtools/webconsole/client.js b/toolkit/devtools/webconsole/client.js index 7ffa5cc89625..478eabc909ba 100644 --- a/toolkit/devtools/webconsole/client.js +++ b/toolkit/devtools/webconsole/client.js @@ -121,14 +121,17 @@ WebConsoleClient.prototype = { * Cursor location inside the string. Index starts from 0. * @param function aOnResponse * The function invoked when the response is received. + * @param string aFrameActor + * The id of the frame actor that made the call. */ - autocomplete: function WCC_autocomplete(aString, aCursor, aOnResponse) + autocomplete: function WCC_autocomplete(aString, aCursor, aOnResponse, aFrameActor) { let packet = { to: this._actor, type: "autocomplete", text: aString, cursor: aCursor, + frameActor: aFrameActor, }; this._client.request(packet, aOnResponse); }, diff --git a/toolkit/devtools/webconsole/utils.js b/toolkit/devtools/webconsole/utils.js index 36348965dbd2..bc224791950c 100644 --- a/toolkit/devtools/webconsole/utils.js +++ b/toolkit/devtools/webconsole/utils.js @@ -741,10 +741,14 @@ function findCompletionBeginning(aStr) /** * Provides a list of properties, that are possible matches based on the passed - * scope and inputValue. + * Debugger.Environment/Debugger.Object and inputValue. * - * @param object aScope - * Scope to use for the completion. + * @param object aDbgObject + * When the debugger is not paused this Debugger.Object wraps the scope for autocompletion. + * It is null if the debugger is paused. + * @param object anEnvironment + * When the debugger is paused this Debugger.Environment is the scope for autocompletion. + * It is null if the debugger is not paused. * @param string aInputValue * Value that should be completed. * @param number [aCursor=aInputValue.length] @@ -760,14 +764,13 @@ function findCompletionBeginning(aStr) * the matches-strings. * } */ -function JSPropertyProvider(aScope, aInputValue, aCursor) +function JSPropertyProvider(aDbgObject, anEnvironment, aInputValue, aCursor) { if (aCursor === undefined) { aCursor = aInputValue.length; } let inputValue = aInputValue.substring(0, aCursor); - let obj = WCU.unwrap(aScope); // Analyse the inputValue and find the beginning of the last part that // should be completed. @@ -799,69 +802,221 @@ function JSPropertyProvider(aScope, aInputValue, aCursor) (completionPart[0] == "'" || completionPart[0] == '"') && completionPart[lastDot - 1] == completionPart[0]) { // We are completing a string literal. - obj = obj.String.prototype; + let obj = String.prototype; matchProp = completionPart.slice(lastDot + 1); + let matches = Object.keys(getMatchedProps(obj, {matchProp:matchProp})); + return { + matchProp: matchProp, + matches: matches, + }; } else { // We are completing a variable / a property lookup. - let properties = completionPart.split("."); if (properties.length > 1) { matchProp = properties.pop().trimLeft(); - for (let i = 0; i < properties.length; i++) { + let obj; + + //The first property must be found in the environment or the Debugger.Object + //depending of whether the debugger is paused or not + let prop = properties[0]; + if (anEnvironment) { + obj = getVariableInEnvironment(anEnvironment, prop); + } + else { + obj = getPropertyInDebuggerObject(aDbgObject, prop); + } + if (obj == null) { + return null; + } + + //We get the rest of the properties recursively starting from the Debugger.Object + // that wraps the first property + for (let i = 1; i < properties.length; i++) { let prop = properties[i].trim(); if (!prop) { return null; } + obj = getPropertyInDebuggerObject(obj, prop); + // If obj is undefined or null (which is what "== null" does), // then there is no chance to run completion on it. Exit here. if (obj == null) { return null; } - - // Check if prop is a getter function on obj. Functions can change other - // stuff so we can't execute them to get the next object. Stop here. - if (WCU.isNonNativeGetter(obj, prop)) { - return null; - } - try { - obj = obj[prop]; - } - catch (ex) { - return null; - } } + + // If the final property is a primitive + if (typeof obj != 'object' || obj === null) { + matchProp = completionPart.slice(lastDot + 1); + let matches = Object.keys(getMatchedProps(obj, {matchProp:matchProp})); + + return { + matchProp: matchProp, + matches: matches, + }; + } + return getMatchedPropsInDbgObject(obj, matchProp); } else { matchProp = properties[0].trimLeft(); - } + if (anEnvironment) { + return getMatchedPropsInEnvironment(anEnvironment, matchProp); + } + else { + if (typeof aDbgObject != 'object' || aDbgObject === null) { + matchProp = completionPart.slice(lastDot + 1); + let matches = Object.keys(getMatchedProps(aDbgObject, {matchProp:matchProp})); - // If obj is undefined or null (which is what "== null" does), - // then there is no chance to run completion on it. Exit here. - if (obj == null) { - return null; + return { + matchProp: matchProp, + matches: matches, + }; + } + return getMatchedPropsInDbgObject(aDbgObject, matchProp); + } } + } +} +/** + * Returns the value of aProp in anEnvironment as a debuggee value, by recursively checking the environment chain + * + * @param object anEnvironment + * A Debugger.Environment to look the aProp into. + * @param string aProp + * The property that is looked up. + * @returns null or object + * A Debugger.Object if aProp exists in the environment chain, null otherwise. + */ +function getVariableInEnvironment(anEnvironment, aProp) +{ + for (let env = anEnvironment; env; env = env.parent) { try { - // Skip Iterators and Generators. - if (WCU.isIteratorOrGenerator(obj)) { - return null; + let obj = env.getVariable(aProp); + if (obj) { + return obj; } } catch (ex) { - // The above can throw if |obj| is a dead object. - // TODO: we should use Cu.isDeadWrapper() - see bug 885800. return null; } } + return null; +} - let matches = Object.keys(getMatchedProps(obj, {matchProp:matchProp})); +/** + * Returns the value of aProp in aDbgObject as a debuggee value, by recursively checking the prototype chain + * + * @param object aDbgObject + * A Debugger.Object to look the aProp into. + * @param string aProp + * The property that is looked up. + * @returns null or object + * A Debugger.Object if aProp exists in the prototype chain, null otherwise. + */ +function getPropertyInDebuggerObject(aDbgObject, aProp) +{ + let dbgObject = aDbgObject; + while (dbgObject) { + try { + let desc = dbgObject.getOwnPropertyDescriptor(aProp) + if (desc) { + let obj = desc.value; + if (obj) + return obj; + obj = desc.get; + if (obj) + return obj; + } + dbgObject = dbgObject.proto; + } + catch (ex) { + return null; + } + } + return null; +} +/** + * Get all properties on the given Debugger.Environment (and its parent chain) that match a given prefix. + * + * @param Debugger.Environment anEnvironment + * Debugger.Environment whose properties we want to filter. + * + * @param string matchProp Filter for properties that match this one. + * + * @return object + * Object that contains the matchProp and the list of names. + */ +function getMatchedPropsInEnvironment(anEnvironment, matchProp) +{ + let names = Object.create(null); + let c = MAX_COMPLETIONS; + for (let env = anEnvironment; env; env = env.parent) { + let ownNames = env.names(); + for (let i = 0; i < ownNames.length; i++) { + if (ownNames[i].indexOf(matchProp) != 0 || + ownNames[i] in names) { + continue; + } + c--; + if (c < 0) { + return { + matchProp: matchProp, + matches: Object.keys(names) + }; + } + names[ownNames[i]] = true; + } + } return { matchProp: matchProp, - matches: matches, + matches: Object.keys(names) + }; +} + +/** + * Get all properties on the given Debugger.Object (and the prototype chain of the wrapped value) that match a given prefix. + * + * @param Debugger.Object aDbgObject + * Debugger.Object whose properties we want to filter. + * + * @param string matchProp Filter for properties that match this one. + * + * @return object + * Object that contains the matchProp and the list of names. + */ +function getMatchedPropsInDbgObject(aDbgObject, matchProp) +{ + let names = Object.create(null); + let c = MAX_COMPLETIONS; + for (let dbg = aDbgObject; dbg; dbg = dbg.proto) { + let raw = dbg.unsafeDereference(); + if (Cu.isDeadWrapper(raw)) { + return null; + } + let ownNames = dbg.getOwnPropertyNames(); + for (let i = 0; i < ownNames.length; i++) { + if (ownNames[i].indexOf(matchProp) != 0 || + ownNames[i] in names) { + continue; + } + c--; + if (c < 0) { + return { + matchProp: matchProp, + matches: Object.keys(names) + }; + } + names[ownNames[i]] = true; + } + } + return { + matchProp: matchProp, + matches: Object.keys(names) }; } diff --git a/toolkit/mozapps/extensions/AddonManager.jsm b/toolkit/mozapps/extensions/AddonManager.jsm index ed4311bd4440..bbf91cacfd3e 100644 --- a/toolkit/mozapps/extensions/AddonManager.jsm +++ b/toolkit/mozapps/extensions/AddonManager.jsm @@ -2279,6 +2279,8 @@ this.AddonManager = { UPDATE_STATUS_UNKNOWN_FORMAT: -4, // The update information was not correctly signed or there was an SSL error. UPDATE_STATUS_SECURITY_ERROR: -5, + // The update was cancelled. + UPDATE_STATUS_CANCELLED: -6, // Constants to indicate why an update check is being performed // Update check has been requested by the user. diff --git a/toolkit/mozapps/extensions/AddonUpdateChecker.jsm b/toolkit/mozapps/extensions/AddonUpdateChecker.jsm index 8e13741f736b..46d269bb4e19 100644 --- a/toolkit/mozapps/extensions/AddonUpdateChecker.jsm +++ b/toolkit/mozapps/extensions/AddonUpdateChecker.jsm @@ -490,8 +490,14 @@ UpdateParser.prototype = { this.notifyError(AddonUpdateChecker.ERROR_PARSE_ERROR); return; } - if ("onUpdateCheckComplete" in this.observer) - this.observer.onUpdateCheckComplete(results); + if ("onUpdateCheckComplete" in this.observer) { + try { + this.observer.onUpdateCheckComplete(results); + } + catch (e) { + WARN("onUpdateCheckComplete notification failed", e); + } + } return; } @@ -534,8 +540,14 @@ UpdateParser.prototype = { * Helper method to notify the observer that an error occured. */ notifyError: function UP_notifyError(aStatus) { - if ("onUpdateCheckError" in this.observer) - this.observer.onUpdateCheckError(aStatus); + if ("onUpdateCheckError" in this.observer) { + try { + this.observer.onUpdateCheckError(aStatus); + } + catch (e) { + WARN("onUpdateCheckError notification failed", e); + } + } }, /** @@ -549,6 +561,17 @@ UpdateParser.prototype = { WARN("Request timed out"); this.notifyError(AddonUpdateChecker.ERROR_TIMEOUT); + }, + + /** + * Called to cancel an in-progress update check. + */ + cancel: function UP_cancel() { + this.timer.cancel(); + this.timer = null; + this.request.abort(); + this.request = null; + this.notifyError(AddonUpdateChecker.ERROR_CANCELLED); } }; @@ -610,6 +633,8 @@ this.AddonUpdateChecker = { ERROR_UNKNOWN_FORMAT: -4, // The update information was not correctly signed or there was an SSL error. ERROR_SECURITY_ERROR: -5, + // The update was cancelled + ERROR_CANCELLED: -6, /** * Retrieves the best matching compatibility update for the application from @@ -721,9 +746,11 @@ this.AddonUpdateChecker = { * The URL of the add-on's update manifest * @param aObserver * An observer to notify of results + * @return UpdateParser so that the caller can use UpdateParser.cancel() to shut + * down in-progress update requests */ checkForUpdates: function AUC_checkForUpdates(aId, aUpdateKey, aUrl, aObserver) { - new UpdateParser(aId, aUpdateKey, aUrl, aObserver); + return new UpdateParser(aId, aUpdateKey, aUrl, aObserver); } }; diff --git a/toolkit/mozapps/extensions/XPIProvider.jsm b/toolkit/mozapps/extensions/XPIProvider.jsm index c4fef5e99ccf..30aaaf0e174c 100644 --- a/toolkit/mozapps/extensions/XPIProvider.jsm +++ b/toolkit/mozapps/extensions/XPIProvider.jsm @@ -1683,6 +1683,24 @@ function directoryStateDiffers(aState, aCache) return false; } +/** + * Wraps a function in an exception handler to protect against exceptions inside callbacks + * @param aFunction function(args...) + * @return function(args...), a function that takes the same arguments as aFunction + * and returns the same result unless aFunction throws, in which case it logs + * a warning and returns undefined. + */ +function makeSafe(aFunction) { + return function(...aArgs) { + try { + return aFunction(...aArgs); + } + catch(ex) { + WARN("XPIProvider callback failed", ex); + } + return undefined; + } +} var XPIProvider = { // An array of known install locations @@ -1734,6 +1752,32 @@ var XPIProvider = { this._telemetryDetails[aId][aName] = aValue; }, + // Keep track of in-progress operations that support cancel() + _inProgress: new Set(), + + doing: function XPI_doing(aCancellable) { + this._inProgress.add(aCancellable); + }, + + done: function XPI_done(aCancellable) { + return this._inProgress.delete(aCancellable); + }, + + cancelAll: function XPI_cancelAll() { + // Cancelling one may alter _inProgress, so restart the iterator after each + while (this._inProgress.size > 0) { + for (let c of this._inProgress) { + try { + c.cancel(); + } + catch (e) { + WARN("Cancel failed", e); + } + this._inProgress.delete(c); + } + } + }, + /** * Adds or updates a URI mapping for an Addon.id. * @@ -2076,6 +2120,9 @@ var XPIProvider = { shutdown: function XPI_shutdown() { LOG("shutdown"); + // Stop anything we were doing asynchronously + this.cancelAll(); + this.bootstrappedAddons = {}; this.bootstrapScopes = {}; this.enabledAddons = null; @@ -3308,7 +3355,7 @@ var XPIProvider = { locMigrateData = XPIDatabase.migrateData[installLocation.name]; for (let id in addonStates) { changed = addMetadata(installLocation, id, addonStates[id], - locMigrateData[id] || null) || changed; + (locMigrateData[id] || null)) || changed; } } @@ -3642,10 +3689,7 @@ var XPIProvider = { */ getAddonByID: function XPI_getAddonByID(aId, aCallback) { XPIDatabase.getVisibleAddonForID (aId, function getAddonByID_getVisibleAddonForID(aAddon) { - if (aAddon) - aCallback(createWrapper(aAddon)); - else - aCallback(null); + aCallback(createWrapper(aAddon)); }); }, @@ -3673,10 +3717,7 @@ var XPIProvider = { */ getAddonBySyncGUID: function XPI_getAddonBySyncGUID(aGUID, aCallback) { XPIDatabase.getAddonBySyncGUID(aGUID, function getAddonBySyncGUID_getAddonBySyncGUID(aAddon) { - if (aAddon) - aCallback(createWrapper(aAddon)); - else - aCallback(null); + aCallback(createWrapper(aAddon)); }); }, @@ -4382,6 +4423,11 @@ var XPIProvider = { if ("_hasResourceCache" in aAddon) aAddon._hasResourceCache = new Map(); + if (aAddon._updateCheck) { + LOG("Cancel in-progress update check for " + aAddon.id); + aAddon._updateCheck.cancel(); + } + // Inactive add-ons don't require a restart to uninstall let requiresRestart = this.uninstallRequiresRestart(aAddon); @@ -4631,6 +4677,7 @@ AddonInstall.prototype = { * The callback to pass the initialised AddonInstall to */ initLocalInstall: function AI_initLocalInstall(aCallback) { + aCallback = makeSafe(aCallback); this.file = this.sourceURI.QueryInterface(Ci.nsIFileURL).file; if (!this.file.exists()) { @@ -4746,7 +4793,7 @@ AddonInstall.prototype = { AddonManagerPrivate.callInstallListeners("onNewInstall", this.listeners, this.wrapper); - aCallback(this); + makeSafe(aCallback)(this); }, /** @@ -4946,7 +4993,7 @@ AddonInstall.prototype = { if (!addon) { // No valid add-on was found - aCallback(); + makeSafe(aCallback)(); return; } @@ -4995,7 +5042,7 @@ AddonInstall.prototype = { }, this); } else { - aCallback(); + makeSafe(aCallback)(); } }, @@ -5009,6 +5056,7 @@ AddonInstall.prototype = { * XPI is incorrectly signed */ loadManifest: function AI_loadManifest(aCallback) { + aCallback = makeSafe(aCallback); let self = this; function addRepositoryData(aAddon) { // Try to load from the existing cache first @@ -5680,7 +5728,7 @@ AddonInstall.createInstall = function AI_createInstall(aCallback, aFile) { } catch(e) { ERROR("Error creating install", e); - aCallback(null); + makeSafe(aCallback)(null); } }; @@ -5819,6 +5867,8 @@ function UpdateChecker(aAddon, aListener, aReason, aAppVersion, aPlatformVersion Components.utils.import("resource://gre/modules/AddonUpdateChecker.jsm"); this.addon = aAddon; + aAddon._updateCheck = this; + XPIProvider.doing(this); this.listener = aListener; this.appVersion = aAppVersion; this.platformVersion = aPlatformVersion; @@ -5842,8 +5892,8 @@ function UpdateChecker(aAddon, aListener, aReason, aAppVersion, aPlatformVersion aReason |= UPDATE_TYPE_NEWVERSION; let url = escapeAddonURI(aAddon, updateURL, aReason, aAppVersion); - AddonUpdateChecker.checkForUpdates(aAddon.id, aAddon.updateKey, - url, this); + this._parser = AddonUpdateChecker.checkForUpdates(aAddon.id, aAddon.updateKey, + url, this); } UpdateChecker.prototype = { @@ -5868,7 +5918,7 @@ UpdateChecker.prototype = { this.listener[aMethod].apply(this.listener, aArgs); } catch (e) { - LOG("Exception calling UpdateListener method " + aMethod + ": " + e); + WARN("Exception calling UpdateListener method " + aMethod, e); } }, @@ -5879,6 +5929,8 @@ UpdateChecker.prototype = { * The list of update details for the add-on */ onUpdateCheckComplete: function UC_onUpdateCheckComplete(aUpdates) { + XPIProvider.done(this.addon._updateCheck); + this.addon._updateCheck = null; let AUC = AddonUpdateChecker; let ignoreMaxVersion = false; @@ -5979,9 +6031,23 @@ UpdateChecker.prototype = { * An error status */ onUpdateCheckError: function UC_onUpdateCheckError(aError) { + XPIProvider.done(this.addon._updateCheck); + this.addon._updateCheck = null; this.callListener("onNoCompatibilityUpdateAvailable", createWrapper(this.addon)); this.callListener("onNoUpdateAvailable", createWrapper(this.addon)); this.callListener("onUpdateFinished", createWrapper(this.addon), aError); + }, + + /** + * Called to cancel an in-progress update check + */ + cancel: function UC_cancel() { + let parser = this._parser; + if (parser) { + this._parser = null; + // This will call back to onUpdateCheckError with a CANCELLED error + parser.cancel(); + } } }; @@ -6635,6 +6701,15 @@ function AddonWrapper(aAddon) { new UpdateChecker(aAddon, aListener, aReason, aAppVersion, aPlatformVersion); }; + // Returns true if there was an update in progress, false if there was no update to cancel + this.cancelUpdate = function AddonWrapper_cancelUpdate() { + if (aAddon._updateCheck) { + aAddon._updateCheck.cancel(); + return true; + } + return false; + }; + this.hasResource = function AddonWrapper_hasResource(aPath) { if (aAddon._hasResourceCache.has(aPath)) return aAddon._hasResourceCache.get(aPath); diff --git a/toolkit/mozapps/extensions/XPIProviderUtils.js b/toolkit/mozapps/extensions/XPIProviderUtils.js index ec65d5cf8986..007a70147ee8 100644 --- a/toolkit/mozapps/extensions/XPIProviderUtils.js +++ b/toolkit/mozapps/extensions/XPIProviderUtils.js @@ -146,10 +146,10 @@ function getRepositoryAddon(aAddon, aCallback) { /** * Wrap an API-supplied function in an exception handler to make it safe to call */ -function safeCallback(aCallback) { +function makeSafe(aCallback) { return function(...aArgs) { try { - aCallback.apply(null, aArgs); + aCallback(...aArgs); } catch(ex) { WARN("XPI Database callback failed", ex); @@ -1057,12 +1057,12 @@ this.XPIDatabase = { this.asyncLoadDB().then( addonDB => { let addonList = _filterDB(addonDB, aFilter); - asyncMap(addonList, getRepositoryAddon, safeCallback(aCallback)); + asyncMap(addonList, getRepositoryAddon, makeSafe(aCallback)); }) .then(null, error => { ERROR("getAddonList failed", e); - safeCallback(aCallback)([]); + makeSafe(aCallback)([]); }); }, @@ -1077,12 +1077,12 @@ this.XPIDatabase = { getAddon: function(aFilter, aCallback) { return this.asyncLoadDB().then( addonDB => { - getRepositoryAddon(_findAddon(addonDB, aFilter), safeCallback(aCallback)); + getRepositoryAddon(_findAddon(addonDB, aFilter), makeSafe(aCallback)); }) .then(null, error => { ERROR("getAddon failed", e); - safeCallback(aCallback)(null); + makeSafe(aCallback)(null); }); }, @@ -1112,7 +1112,7 @@ this.XPIDatabase = { getAddonInLocation: function XPIDB_getAddonInLocation(aId, aLocation, aCallback) { this.asyncLoadDB().then( addonDB => getRepositoryAddon(addonDB.get(aLocation + ":" + aId), - safeCallback(aCallback))); + makeSafe(aCallback))); }, /** diff --git a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js index 00f2c628b4c3..f8bc6dbd91c0 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js +++ b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js @@ -16,13 +16,29 @@ const PREF_EM_MIN_COMPAT_PLATFORM_VERSION = "extensions.minCompatiblePlatformVer // Forcibly end the test if it runs longer than 15 minutes const TIMEOUT_MS = 900000; -Components.utils.import("resource://gre/modules/AddonManager.jsm"); Components.utils.import("resource://gre/modules/AddonRepository.jsm"); Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); Components.utils.import("resource://gre/modules/FileUtils.jsm"); Components.utils.import("resource://gre/modules/Services.jsm"); Components.utils.import("resource://gre/modules/NetUtil.jsm"); +// We need some internal bits of AddonManager +let AMscope = Components.utils.import("resource://gre/modules/AddonManager.jsm"); +let AddonManager = AMscope.AddonManager; +let AddonManagerInternal = AMscope.AddonManagerInternal; +// Mock out AddonManager's reference to the AsyncShutdown module so we can shut +// down AddonManager from the test +let MockAsyncShutdown = { + hook: null, + profileBeforeChange: { + addBlocker: function(aName, aBlocker) { + do_print("Mock profileBeforeChange blocker for '" + aName + "'"); + MockAsyncShutdown.hook = aBlocker; + } + } +}; +AMscope.AsyncShutdown = MockAsyncShutdown; + var gInternalManager = null; var gAppInfo = null; var gAddonsList; @@ -403,11 +419,16 @@ function shutdownManager() { let shutdownDone = false; Services.obs.notifyObservers(null, "quit-application-granted", null); - let scope = Components.utils.import("resource://gre/modules/AddonManager.jsm"); - scope.AddonManagerInternal.shutdown() - .then( - () => shutdownDone = true, - err => shutdownDone = true); + MockAsyncShutdown.hook().then( + () => shutdownDone = true, + err => shutdownDone = true); + + let thr = Services.tm.mainThread; + + // Wait until we observe the shutdown notifications + while (!shutdownDone) { + thr.processNextEvent(true); + } gInternalManager = null; @@ -417,20 +438,13 @@ function shutdownManager() { // Clear any crash report annotations gAppInfo.annotations = {}; - let thr = Services.tm.mainThread; - - // Wait until we observe the shutdown notifications - while (!shutdownDone) { - thr.processNextEvent(true); - } - // Force the XPIProvider provider to reload to better // simulate real-world usage. - scope = Components.utils.import("resource://gre/modules/XPIProvider.jsm"); + let XPIscope = Components.utils.import("resource://gre/modules/XPIProvider.jsm"); // This would be cleaner if I could get it as the rejection reason from // the AddonManagerInternal.shutdown() promise - gXPISaveError = scope.XPIProvider._shutdownError; - AddonManagerPrivate.unregisterProvider(scope.XPIProvider); + gXPISaveError = XPIscope.XPIProvider._shutdownError; + AddonManagerPrivate.unregisterProvider(XPIscope.XPIProvider); Components.utils.unload("resource://gre/modules/XPIProvider.jsm"); } @@ -1090,16 +1104,24 @@ if ("nsIWindowsRegKey" in AM_Ci) { var MockRegistry = { LOCAL_MACHINE: {}, CURRENT_USER: {}, + CLASSES_ROOT: {}, - setValue: function(aRoot, aPath, aName, aValue) { + getRoot: function(aRoot) { switch (aRoot) { case AM_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE: - var rootKey = MockRegistry.LOCAL_MACHINE; - break + return MockRegistry.LOCAL_MACHINE; case AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER: - rootKey = MockRegistry.CURRENT_USER; - break + return MockRegistry.CURRENT_USER; + case AM_Ci.nsIWindowsRegKey.ROOT_KEY_CLASSES_ROOT: + return MockRegistry.CLASSES_ROOT; + default: + do_throw("Unknown root " + aRootKey); + return null; } + }, + + setValue: function(aRoot, aPath, aName, aValue) { + let rootKey = MockRegistry.getRoot(aRoot); if (!(aPath in rootKey)) { rootKey[aPath] = []; @@ -1141,14 +1163,7 @@ if ("nsIWindowsRegKey" in AM_Ci) { // --- Overridden nsIWindowsRegKey interface functions --- open: function(aRootKey, aRelPath, aMode) { - switch (aRootKey) { - case AM_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE: - var rootKey = MockRegistry.LOCAL_MACHINE; - break - case AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER: - rootKey = MockRegistry.CURRENT_USER; - break - } + let rootKey = MockRegistry.getRoot(aRootKey); if (!(aRelPath in rootKey)) rootKey[aRelPath] = []; @@ -1398,9 +1413,9 @@ function changeXPIDBVersion(aNewVersion) { } /** - * Raw load of a JSON file + * Load a file into a string */ -function loadJSON(aFile) { +function loadFile(aFile) { let data = ""; let fstream = Components.classes["@mozilla.org/network/file-input-stream;1"]. createInstance(Components.interfaces.nsIFileInputStream); @@ -1416,6 +1431,14 @@ function loadJSON(aFile) { } while (read != 0); } cstream.close(); + return data; +} + +/** + * Raw load of a JSON file + */ +function loadJSON(aFile) { + let data = loadFile(aFile); do_print("Loaded JSON file " + aFile.path); return(JSON.parse(data)); } diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_XPIcancel.js b/toolkit/mozapps/extensions/test/xpcshell/test_XPIcancel.js new file mode 100644 index 000000000000..99aa9422cbed --- /dev/null +++ b/toolkit/mozapps/extensions/test/xpcshell/test_XPIcancel.js @@ -0,0 +1,66 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// Test the cancellable doing/done/cancelAll API in XPIProvider + +let scope = Components.utils.import("resource://gre/modules/XPIProvider.jsm"); +let XPIProvider = scope.XPIProvider; + +function run_test() { + // Check that cancelling with nothing in progress doesn't blow up + XPIProvider.cancelAll(); + + // Check that a basic object gets cancelled + let getsCancelled = { + isCancelled: false, + cancel: function () { + if (this.isCancelled) + do_throw("Already cancelled"); + this.isCancelled = true; + } + }; + XPIProvider.doing(getsCancelled); + XPIProvider.cancelAll(); + do_check_true(getsCancelled.isCancelled); + + // Check that if we complete a cancellable, it doesn't get cancelled + let doesntGetCancelled = { + cancel: () => do_throw("This should not have been cancelled") + }; + XPIProvider.doing(doesntGetCancelled); + do_check_true(XPIProvider.done(doesntGetCancelled)); + XPIProvider.cancelAll(); + + // A cancellable that adds a cancellable + getsCancelled.isCancelled = false; + let addsAnother = { + isCancelled: false, + cancel: function () { + if (this.isCancelled) + do_throw("Already cancelled"); + this.isCancelled = true; + XPIProvider.doing(getsCancelled); + } + } + XPIProvider.doing(addsAnother); + XPIProvider.cancelAll(); + do_check_true(addsAnother.isCancelled); + do_check_true(getsCancelled.isCancelled); + + // A cancellable that removes another. This assumes that Set() iterates in the + // order that members were added + let removesAnother = { + isCancelled: false, + cancel: function () { + if (this.isCancelled) + do_throw("Already cancelled"); + this.isCancelled = true; + XPIProvider.done(doesntGetCancelled); + } + } + XPIProvider.doing(removesAnother); + XPIProvider.doing(doesntGetCancelled); + XPIProvider.cancelAll(); + do_check_true(removesAnother.isCancelled); +} diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_updateCancel.js b/toolkit/mozapps/extensions/test/xpcshell/test_updateCancel.js new file mode 100644 index 000000000000..da3636921534 --- /dev/null +++ b/toolkit/mozapps/extensions/test/xpcshell/test_updateCancel.js @@ -0,0 +1,149 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// Test cancelling add-on update checks while in progress (bug 925389) + +Components.utils.import("resource://gre/modules/Promise.jsm"); + +// The test extension uses an insecure update url. +Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false); +Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false); + +createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2"); + +// Set up an HTTP server to respond to update requests +Components.utils.import("resource://testing-common/httpd.js"); + +const profileDir = gProfD.clone(); +profileDir.append("extensions"); + +// Return a promise that resolves with an addon retrieved by +// AddonManager.getAddonByID() +function promiseGetAddon(aID) { + let p = Promise.defer(); + AddonManager.getAddonByID(aID, p.resolve); + return p.promise; +} + +function run_test() { + // Kick off the task-based tests... + run_next_test(); +} + +// Install one extension +// Start download of update check (but delay HTTP response) +// Cancel update check +// - ensure we get cancel notification +// complete HTTP response +// - ensure no callbacks after cancel +// - ensure update is gone + +// Create an addon update listener containing a promise +// that resolves when the update is cancelled +function makeCancelListener() { + let updated = Promise.defer(); + return { + onUpdateAvailable: function(addon, install) { + updated.reject("Should not have seen onUpdateAvailable notification"); + }, + + onUpdateFinished: function(aAddon, aError) { + do_print("onUpdateCheckFinished: " + aAddon.id + " " + aError); + updated.resolve(aError); + }, + promise: updated.promise + }; +} + +// Set up the HTTP server so that we can control when it responds +let httpReceived = Promise.defer(); +function dataHandler(aRequest, aResponse) { + asyncResponse = aResponse; + aResponse.processAsync(); + httpReceived.resolve([aRequest, aResponse]); +} +var testserver = new HttpServer(); +testserver.registerDirectory("/addons/", do_get_file("addons")); +testserver.registerPathHandler("/data/test_update.rdf", dataHandler); +testserver.start(-1); +gPort = testserver.identity.primaryPort; + +// Set up an add-on for update check +writeInstallRDFForExtension({ + id: "addon1@tests.mozilla.org", + version: "1.0", + updateURL: "http://localhost:" + gPort + "/data/test_update.rdf", + targetApplications: [{ + id: "xpcshell@tests.mozilla.org", + minVersion: "1", + maxVersion: "1" + }], + name: "Test Addon 1", +}, profileDir); + +add_task(function cancel_during_check() { + startupManager(); + + let a1 = yield promiseGetAddon("addon1@tests.mozilla.org"); + do_check_neq(a1, null); + + let listener = makeCancelListener(); + a1.findUpdates(listener, AddonManager.UPDATE_WHEN_USER_REQUESTED); + + // Wait for the http request to arrive + let [request, response] = yield httpReceived.promise; + + // cancelUpdate returns true if there is an update check in progress + do_check_true(a1.cancelUpdate()); + + let updateResult = yield listener.promise; + do_check_eq(AddonManager.UPDATE_STATUS_CANCELLED, updateResult); + + // Now complete the HTTP request + let file = do_get_cwd(); + file.append("data"); + file.append("test_update.rdf"); + let data = loadFile(file); + response.write(data); + response.finish(); + + // trying to cancel again should return false, i.e. nothing to cancel + do_check_false(a1.cancelUpdate()); + + yield true; +}); + +// Test that update check is cancelled if the XPI provider shuts down while +// the update check is in progress +add_task(function shutdown_during_check() { + // Reset our HTTP listener + httpReceived = Promise.defer(); + + let a1 = yield promiseGetAddon("addon1@tests.mozilla.org"); + do_check_neq(a1, null); + + let listener = makeCancelListener(); + a1.findUpdates(listener, AddonManager.UPDATE_WHEN_USER_REQUESTED); + + // Wait for the http request to arrive + let [request, response] = yield httpReceived.promise; + + shutdownManager(); + + let updateResult = yield listener.promise; + do_check_eq(AddonManager.UPDATE_STATUS_CANCELLED, updateResult); + + // Now complete the HTTP request + let file = do_get_cwd(); + file.append("data"); + file.append("test_update.rdf"); + let data = loadFile(file); + response.write(data); + response.finish(); + + // trying to cancel again should return false, i.e. nothing to cancel + do_check_false(a1.cancelUpdate()); + + yield testserver.stop(Promise.defer().resolve); +}); diff --git a/toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini b/toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini index d9d1ca90146c..dcc8eaeca0ca 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini +++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini @@ -11,6 +11,7 @@ run-sequentially = Uses hardcoded ports in xpi files. skip-if = os == "android" [test_DeferredSave.js] [test_LightweightThemeManager.js] +[test_XPIcancel.js] [test_backgroundupdate.js] [test_bad_json.js] [test_badschema.js] @@ -230,6 +231,7 @@ fail-if = os == "android" [test_update.js] # Bug 676992: test consistently hangs on Android skip-if = os == "android" +[test_updateCancel.js] [test_update_strictcompat.js] # Bug 676992: test consistently hangs on Android skip-if = os == "android" diff --git a/widget/BasicEvents.h b/widget/BasicEvents.h index 2dc93b035bd0..e9093fec5227 100644 --- a/widget/BasicEvents.h +++ b/widget/BasicEvents.h @@ -460,6 +460,10 @@ enum nsEventStructType #define NS_MEDIARECORDER_WARNING (NS_MEDIARECORDER_EVENT_START + 2) #define NS_MEDIARECORDER_STOP (NS_MEDIARECORDER_EVENT_START + 3) +// SpeakerManager events +#define NS_SPEAKERMANAGER_EVENT_START 5800 +#define NS_SPEAKERMANAGER_SPEAKERFORCEDCHANGE (NS_SPEAKERMANAGER_EVENT_START + 1) + #ifdef MOZ_GAMEPAD // Gamepad input events #define NS_GAMEPAD_START 6000 diff --git a/widget/android/nsAppShell.cpp b/widget/android/nsAppShell.cpp index 27e8a3a1dd2e..f0aca7980dcc 100644 --- a/widget/android/nsAppShell.cpp +++ b/widget/android/nsAppShell.cpp @@ -312,7 +312,14 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait) mozilla::hal::SensorType type = (mozilla::hal::SensorType) curEvent->Flags(); switch (type) { + // Bug 938035, transfer HAL data for orientation sensor to meet w3c + // spec, ex: HAL report alpha=90 means East but alpha=90 means West + // in w3c spec case hal::SENSOR_ORIENTATION: + values.AppendElement(360 -curEvent->X()); + values.AppendElement(-curEvent->Y()); + values.AppendElement(-curEvent->Z()); + break; case hal::SENSOR_LINEAR_ACCELERATION: case hal::SENSOR_ACCELERATION: case hal::SENSOR_GYROSCOPE: diff --git a/widget/windows/winrt/APZController.cpp b/widget/windows/winrt/APZController.cpp index 73e31437de6f..be49a4eed68f 100644 --- a/widget/windows/winrt/APZController.cpp +++ b/widget/windows/winrt/APZController.cpp @@ -317,18 +317,44 @@ APZController::PostDelayedTask(Task* aTask, int aDelayMs) MessageLoop::current()->PostDelayedTask(FROM_HERE, aTask, aDelayMs); } -// async scroll notifications +// apzc notifications + +class TransformedStartEvent : public nsRunnable +{ + NS_IMETHOD Run() { + MetroUtils::FireObserver("apzc-transform-start", L""); + return NS_OK; + } +}; + +class TransformedEndEvent : public nsRunnable +{ + NS_IMETHOD Run() { + MetroUtils::FireObserver("apzc-transform-end", L""); + return NS_OK; + } +}; void -APZController::HandlePanBegin() +APZController::NotifyTransformBegin() { - MetroUtils::FireObserver("apzc-handle-pan-begin", L""); + if (NS_IsMainThread()) { + MetroUtils::FireObserver("apzc-transform-begin", L""); + return; + } + nsCOMPtr runnable = new TransformedStartEvent(); + NS_DispatchToMainThread(runnable); } void -APZController::HandlePanEnd() +APZController::NotifyTransformEnd() { - MetroUtils::FireObserver("apzc-handle-pan-end", L""); + if (NS_IsMainThread()) { + MetroUtils::FireObserver("apzc-transform-end", L""); + return; + } + nsCOMPtr runnable = new TransformedEndEvent(); + NS_DispatchToMainThread(runnable); } } } } diff --git a/widget/windows/winrt/APZController.h b/widget/windows/winrt/APZController.h index f15880d25ea0..cff4bc322370 100644 --- a/widget/windows/winrt/APZController.h +++ b/widget/windows/winrt/APZController.h @@ -37,8 +37,8 @@ public: virtual void HandleLongTap(const mozilla::CSSIntPoint& aPoint, int32_t aModifiers); virtual void SendAsyncScrollDOMEvent(bool aIsRoot, const mozilla::CSSRect &aContentRect, const mozilla::CSSSize &aScrollableSize); virtual void PostDelayedTask(Task* aTask, int aDelayMs); - virtual void HandlePanBegin(); - virtual void HandlePanEnd(); + virtual void NotifyTransformBegin(); + virtual void NotifyTransformEnd(); void SetWidgetListener(nsIWidgetListener* aWidgetListener); void UpdateScrollOffset(const mozilla::layers::ScrollableLayerGuid& aScrollLayerId, CSSIntPoint& aScrollOffset); diff --git a/widget/windows/winrt/MetroAppShell.cpp b/widget/windows/winrt/MetroAppShell.cpp index 7ae81c3599c8..b8bb540d522e 100644 --- a/widget/windows/winrt/MetroAppShell.cpp +++ b/widget/windows/winrt/MetroAppShell.cpp @@ -43,7 +43,6 @@ extern UINT sAppShellGeckoMsgId; static ComPtr sCoreStatic; static bool sIsDispatching = false; static bool sWillEmptyThreadQueue = false; -static bool sEmptyingThreadQueue = false; MetroAppShell::~MetroAppShell() { @@ -242,12 +241,9 @@ MetroAppShell::DispatchAllGeckoEvents() return; } - NS_ASSERTION(NS_IsMainThread(), "DispatchAllXPCOMEvents should be called on the main thread"); + NS_ASSERTION(NS_IsMainThread(), "DispatchAllGeckoEvents should be called on the main thread"); sWillEmptyThreadQueue = false; - - AutoRestore dispatching(sEmptyingThreadQueue); - sEmptyingThreadQueue = true; nsIThread *thread = NS_GetCurrentThread(); NS_ProcessPendingEvents(thread, 0); } @@ -297,14 +293,6 @@ MetroAppShell::ProcessOneNativeEventIfPresent() bool MetroAppShell::ProcessNextNativeEvent(bool mayWait) { - // NS_ProcessPendingEvents will process thread events *and* call - // nsBaseAppShell::OnProcessNextEvent to process native events. However - // we do not want native events getting dispatched while we are in - // DispatchAllGeckoEvents. - if (sEmptyingThreadQueue) { - return false; - } - if (ProcessOneNativeEventIfPresent()) { return true; } diff --git a/widget/windows/winrt/MetroInput.cpp b/widget/windows/winrt/MetroInput.cpp index a36ede4afd68..3e049854c47c 100644 --- a/widget/windows/winrt/MetroInput.cpp +++ b/widget/windows/winrt/MetroInput.cpp @@ -496,6 +496,9 @@ MetroInput::OnPointerPressed(UI::Core::ICoreWindow* aSender, mRecognizerWantsEvents = true; mCancelable = true; mCanceledIds.Clear(); + } else { + // Only the first touchstart can be canceled. + mCancelable = false; } InitTouchEventTouchList(touchEvent); @@ -1141,7 +1144,7 @@ MetroInput::DeliverNextQueuedTouchEvent() // Test for chrome vs. content target. To do this we only use the first touch // point since that will be the input batch target. Cache this for touch events // since HitTestChrome has to send a dom event. - if (mCancelable && event->message == NS_TOUCH_START && mTouches.Count() == 1) { + if (mCancelable && event->message == NS_TOUCH_START) { nsRefPtr touch = event->touches[0]; LayoutDeviceIntPoint pt = LayoutDeviceIntPoint::FromUntyped(touch->mRefPoint); bool apzIntersect = mWidget->ApzHitTest(mozilla::ScreenIntPoint(pt.x, pt.y)); @@ -1150,7 +1153,7 @@ MetroInput::DeliverNextQueuedTouchEvent() // If this event is destined for chrome, deliver it directly there bypassing // the apz. - if (mChromeHitTestCacheForTouch) { + if (!mCancelable && mChromeHitTestCacheForTouch) { DUMP_TOUCH_IDS("DOM(1)", event); mWidget->DispatchEvent(event, status); return; @@ -1164,7 +1167,7 @@ MetroInput::DeliverNextQueuedTouchEvent() DUMP_TOUCH_IDS("APZC(1)", event); mWidget->ApzReceiveInputEvent(event, &mTargetAPZCGuid, &transformedEvent); DUMP_TOUCH_IDS("DOM(2)", event); - mWidget->DispatchEvent(&transformedEvent, status); + mWidget->DispatchEvent(mChromeHitTestCacheForTouch ? event : &transformedEvent, status); if (event->message == NS_TOUCH_START) { mContentConsumingTouch = (nsEventStatus_eConsumeNoDefault == status); // If we know content wants touch here, we can bail early on mCancelable @@ -1207,7 +1210,9 @@ MetroInput::DeliverNextQueuedTouchEvent() if (mContentConsumingTouch) { // Only translate if we're dealing with web content that's transformed // by the apzc. - TransformTouchEvent(event); + if (!mChromeHitTestCacheForTouch) { + TransformTouchEvent(event); + } DUMP_TOUCH_IDS("DOM(3)", event); mWidget->DispatchEvent(event, status); return; @@ -1223,7 +1228,9 @@ MetroInput::DeliverNextQueuedTouchEvent() DispatchTouchCancel(event); return; } - TransformTouchEvent(event); + if (!mChromeHitTestCacheForTouch) { + TransformTouchEvent(event); + } DUMP_TOUCH_IDS("DOM(4)", event); mWidget->DispatchEvent(event, status); } diff --git a/widget/windows/winrt/MetroWidget.cpp b/widget/windows/winrt/MetroWidget.cpp index 90dd965878d0..e750d0045533 100644 --- a/widget/windows/winrt/MetroWidget.cpp +++ b/widget/windows/winrt/MetroWidget.cpp @@ -1631,7 +1631,5 @@ MetroWidget::Observe(nsISupports *subject, const char *topic, const PRUnichar *d ScrollableLayerGuid guid = ScrollableLayerGuid(mRootLayerTreeId, presShellId, viewId); APZController::sAPZC->UpdateZoomConstraints(guid, false, CSSToScreenScale(1.0f), CSSToScreenScale(1.0f)); } - else { - return NS_OK; - } + return NS_OK; }