author | Ryan VanderMeulen <ryanvm@gmail.com> |
Tue, 08 Apr 2014 18:17:58 -0400 | |
changeset 177591 | 5811efc11011e9569fc75a314ae86b04dc287563 |
parent 177535 | 0701321143880d66f8176ddac61ab42c4a4909f4 (current diff) |
parent 177590 | 9f6c65c78caded7bb957fdf204d41a617f03043b (diff) |
child 177592 | 43cd629879c28598037145b5924603eaeb4b9cbd |
child 177620 | 2eec85fb423ad7be1c37ea039cf4828ee2dc3dc5 |
child 177660 | 27fd61f01ff72ad55f40748be2c7a26c668e7bef |
child 177673 | 4853301561bf6833ae1dcfa7cd53ebe203ef3122 |
push id | 26556 |
push user | ryanvm@gmail.com |
push date | Tue, 08 Apr 2014 22:16:57 +0000 |
treeherder | mozilla-central@5811efc11011 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 31.0a1 |
first release with | nightly linux32
5811efc11011
/
31.0a1
/
20140409030203
/
files
nightly linux64
5811efc11011
/
31.0a1
/
20140409030203
/
files
nightly mac
5811efc11011
/
31.0a1
/
20140409030203
/
files
nightly win32
5811efc11011
/
31.0a1
/
20140409030203
/
files
nightly win64
5811efc11011
/
31.0a1
/
20140409030203
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
31.0a1
/
20140409030203
/
pushlog to previous
nightly linux64
31.0a1
/
20140409030203
/
pushlog to previous
nightly mac
31.0a1
/
20140409030203
/
pushlog to previous
nightly win32
31.0a1
/
20140409030203
/
pushlog to previous
nightly win64
31.0a1
/
20140409030203
/
pushlog to previous
|
--- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -917,21 +917,57 @@ chatbox:-moz-full-screen-ancestor > .cha } /* Give this menupopup an arrow panel styling */ #BMB_bookmarksPopup { -moz-appearance: none; -moz-binding: url("chrome://browser/content/places/menu.xml#places-popup-arrow"); background: transparent; border: none; - transition: opacity 300ms; + transform: scale(.7); + opacity: 0; + transition-property: transform, opacity; + transition-duration: 0.15s; + transition-timing-function: ease; /* The popup inherits -moz-image-region from the button, must reset it */ -moz-image-region: auto; } +#BMB_bookmarksPopup[animate="open"] { + transform: none; + opacity: 1.0; +} + +#BMB_bookmarksPopup[arrowposition="after_start"] { + transform-origin: 20px top; +} + +#BMB_bookmarksPopup[arrowposition="after_end"] { + transform-origin: calc(100% - 20px) top; +} + +#BMB_bookmarksPopup[arrowposition="before_start"] { + transform-origin: 20px bottom; +} + +#BMB_bookmarksPopup[arrowposition="before_end"] { + transform-origin: calc(100% - 20px) bottom; +} + +#BMB_bookmarksPopup[arrowposition="after_start"][animate="cancel"], +#BMB_bookmarksPopup[arrowposition="before_end"][animate="cancel"] { + transform: scale(.7) skew(30deg, 20deg); +} + +#BMB_bookmarksPopup[arrowposition="after_end"][animate="cancel"], +#BMB_bookmarksPopup[arrowposition="before_start"][animate="cancel"] { + transform: scale(.7) skew(-30deg, -20deg); +} + + /* Customize mode */ #navigator-toolbox, #browser-bottombox, #content-deck { transition-property: margin-left, margin-right; transition-duration: 200ms; transition-timing-function: linear; }
--- a/browser/base/content/test/general/browser_bug553455.js +++ b/browser/base/content/test/general/browser_bug553455.js @@ -874,20 +874,23 @@ function test() { Services.prefs.setBoolPref("extensions.logging.enabled", true); Services.prefs.setBoolPref("extensions.strictCompatibility", true); Services.obs.addObserver(XPInstallObserver, "addon-install-started", false); Services.obs.addObserver(XPInstallObserver, "addon-install-blocked", false); Services.obs.addObserver(XPInstallObserver, "addon-install-failed", false); Services.obs.addObserver(XPInstallObserver, "addon-install-complete", false); + PopupNotifications.transitionsEnabled = false; + registerCleanupFunction(function() { // Make sure no more test parts run in case we were timed out TESTS = []; PopupNotifications.panel.removeEventListener("popupshown", check_notification, false); + PopupNotifications.transitionsEnabled = true; AddonManager.getAllInstalls(function(aInstalls) { aInstalls.forEach(function(aInstall) { aInstall.cancel(); }); }); Services.prefs.clearUserPref("extensions.logging.enabled");
--- a/browser/base/content/test/general/browser_customize_popupNotification.js +++ b/browser/base/content/test/general/browser_customize_popupNotification.js @@ -7,21 +7,24 @@ function test() { waitForExplicitFinish(); let newWin = OpenBrowserWindow(); whenDelayedStartupFinished(newWin, function () { // Remove the URL bar newWin.gURLBar.parentNode.removeChild(newWin.gURLBar); waitForFocus(function () { let PN = newWin.PopupNotifications; + PN.transitionsEnabled = false; try { let notification = PN.show(newWin.gBrowser.selectedBrowser, "some-notification", "Some message"); ok(notification, "showed the notification"); ok(PN.isPanelOpen, "panel is open"); is(PN.panel.anchorNode, newWin.gBrowser.selectedTab, "notification is correctly anchored to the tab"); + PN.panel.hidePopup(); } catch (ex) { ok(false, "threw exception: " + ex); } + PN.transitionsEnabled = true; newWin.close(); finish(); }, newWin); }); }
--- a/browser/base/content/test/general/browser_popupNotification.js +++ b/browser/base/content/test/general/browser_popupNotification.js @@ -3,27 +3,32 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ function test() { waitForExplicitFinish(); ok(PopupNotifications, "PopupNotifications object exists"); ok(PopupNotifications.panel, "PopupNotifications panel exists"); + // Disable transitions as they slow the test down and we want to click the + // mouse buttons in a predictable location. + PopupNotifications.transitionsEnabled = false; + registerCleanupFunction(cleanUp); runNextTest(); } function cleanUp() { for (var topic in gActiveObservers) Services.obs.removeObserver(gActiveObservers[topic], topic); for (var eventName in gActiveListeners) PopupNotifications.panel.removeEventListener(eventName, gActiveListeners[eventName], false); PopupNotifications.buttonDelay = PREF_SECURITY_DELAY_INITIAL; + PopupNotifications.transitionsEnabled = true; } const PREF_SECURITY_DELAY_INITIAL = Services.prefs.getIntPref("security.notification_enable_delay"); var gActiveListeners = {}; var gActiveObservers = {}; var gShownState = {};
--- a/browser/components/customizableui/content/panelUI.inc.xul +++ b/browser/components/customizableui/content/panelUI.inc.xul @@ -2,16 +2,17 @@ - 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/. --> <panel id="PanelUI-popup" role="group" type="arrow" hidden="true" flip="slide" + animate="false" position="bottomcenter topright" noautofocus="true"> <panelmultiview id="PanelUI-multiView" mainViewId="PanelUI-mainView"> <panelview id="PanelUI-mainView" context="customizationPanelContextMenu"> <vbox id="PanelUI-contents-scroller"> <vbox id="PanelUI-contents" class="panelUI-grid"/> </vbox>
--- a/browser/components/customizableui/test/browser_884402_customize_from_overflow.js +++ b/browser/components/customizableui/test/browser_884402_customize_from_overflow.js @@ -1,22 +1,26 @@ "use strict"; let overflowPanel = document.getElementById("widget-overflow"); const isOSX = (Services.appinfo.OS === "Darwin"); let originalWindowWidth; registerCleanupFunction(function() { + overflowPanel.removeAttribute("animate"); window.resizeTo(originalWindowWidth, window.outerHeight); }); // Right-click on an item within the overflow panel should // show a context menu with options to move it. add_task(function() { + + overflowPanel.setAttribute("animate", "false"); + originalWindowWidth = window.outerWidth; let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR); ok(!navbar.hasAttribute("overflowing"), "Should start with a non-overflowing toolbar."); let oldChildCount = navbar.customizationTarget.childElementCount; window.resizeTo(400, window.outerHeight); yield waitForCondition(() => navbar.hasAttribute("overflowing")); ok(navbar.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
--- a/browser/components/places/content/menu.xml +++ b/browser/components/places/content/menu.xml @@ -521,16 +521,19 @@ return; } var container = document.getAnonymousElementByAttribute(this, "anonid", "container"); var arrowbox = document.getAnonymousElementByAttribute(this, "anonid", "arrowbox"); var position = this.alignmentPosition; var offset = this.alignmentOffset; + + this.setAttribute("arrowposition", position); + // if this panel has a "sliding" arrow, we may have previously set margins... arrowbox.style.removeProperty("transform"); if (position.indexOf("start_") == 0 || position.indexOf("end_") == 0) { container.orient = "horizontal"; arrowbox.orient = "vertical"; if (position.indexOf("_after") > 0) { arrowbox.pack = "end"; } else { @@ -573,16 +576,17 @@ arrow.hidden = false; ]]></body> </method> </implementation> <handlers> <handler event="popupshowing" phase="target"><![CDATA[ this.adjustArrowPosition(); + this.setAttribute("animate", "open"); ]]></handler> <handler event="popupshown" phase="target"><![CDATA[ this.setAttribute("panelopen", "true"); let disablePointerEvents; if (!this.hasAttribute("disablepointereventsfortransition")) { let container = document.getAnonymousElementByAttribute(this, "anonid", "container"); let cs = getComputedStyle(container); let transitionProp = cs.transitionProperty; @@ -599,17 +603,21 @@ } ]]></handler> <handler event="transitionend"><![CDATA[ if (event.originalTarget.getAttribute("anonid") == "container" && event.propertyName == "transform") { this.style.removeProperty("pointer-events"); } ]]></handler> + <handler event="popuphiding" phase="target"><![CDATA[ + this.setAttribute("animate", "cancel"); + ]]></handler> <handler event="popuphidden" phase="target"><![CDATA[ this.removeAttribute("panelopen"); if (this.getAttribute("disablepointereventsfortransition") == "true") { this.style.pointerEvents = 'none'; } + this.removeAttribute("animate"); ]]></handler> </handlers> </binding> </bindings>
--- a/browser/components/places/tests/browser/browser_toolbarbutton_menu_context.js +++ b/browser/components/places/tests/browser/browser_toolbarbutton_menu_context.js @@ -12,27 +12,29 @@ add_task(function testPopup() { CustomizableUI.addWidgetToArea("bookmarks-menu-button", CustomizableUI.AREA_PANEL); CustomizableUI.addWidgetToArea("bookmarks-menu-button", CustomizableUI.AREA_NAVBAR, pos); info("Checking popup context menu after moving the bookmarks button"); yield checkPopupContextMenu(); }); function* checkPopupContextMenu() { let dropmarker = document.getAnonymousElementByAttribute(bookmarksMenuButton, "anonid", "dropmarker"); + BMB_menuPopup.setAttribute("style", "transition: none;"); let popupShownPromise = onPopupEvent(BMB_menuPopup, "shown"); EventUtils.synthesizeMouseAtCenter(dropmarker, {}); info("Waiting for bookmarks menu to be shown."); yield popupShownPromise; let contextMenuShownPromise = onPopupEvent(contextMenu, "shown"); EventUtils.synthesizeMouseAtCenter(BMB_showAllBookmarks, {type: "contextmenu", button: 2 }); info("Waiting for context menu on bookmarks menu to be shown."); yield contextMenuShownPromise; ok(!newBookmarkItem.hasAttribute("disabled"), "New bookmark item shouldn't be disabled"); let contextMenuHiddenPromise = onPopupEvent(contextMenu, "hidden"); contextMenu.hidePopup(); + BMB_menuPopup.removeAttribute("style"); info("Waiting for context menu on bookmarks menu to be hidden."); yield contextMenuHiddenPromise; let popupHiddenPromise = onPopupEvent(BMB_menuPopup, "hidden"); // Can't use synthesizeMouseAtCenter because the dropdown panel is in the way EventUtils.synthesizeKey("VK_ESCAPE", {}); info("Waiting for bookmarks menu to be hidden."); yield popupHiddenPromise; }
--- a/browser/devtools/shared/widgets/Tooltip.js +++ b/browser/devtools/shared/widgets/Tooltip.js @@ -98,16 +98,17 @@ let PanelFactory = { * @param {OptionsStore} options * An options store to get some configuration from */ get: function(doc, options) { // Create the tooltip let panel = doc.createElement("panel"); panel.setAttribute("hidden", true); panel.setAttribute("ignorekeys", true); + panel.setAttribute("animate", false); panel.setAttribute("consumeoutsideclicks", options.get("consumeOutsideClick")); panel.setAttribute("noautofocus", options.get("noAutoFocus")); panel.setAttribute("type", "arrow"); panel.setAttribute("level", "top"); panel.setAttribute("class", "devtools-tooltip theme-tooltip-panel"); doc.querySelector("window").appendChild(panel);
--- a/browser/modules/test/browser_SignInToWebsite.js +++ b/browser/modules/test/browser_SignInToWebsite.js @@ -271,16 +271,18 @@ function test() { try { Components.utils.import("resource:///modules/SignInToWebsite.jsm", sitw); } catch (ex) { ok(true, "Skip the test since SignInToWebsite.jsm isn't packaged outside outside mozilla-central"); finish(); return; } + PopupNotifications.transitionsEnabled = false; + registerCleanupFunction(cleanUp); ok(sitw.SignInToWebsiteUX, "SignInToWebsiteUX object exists"); if (!Services.prefs.getBoolPref("dom.identity.enabled")) { // If the pref isn't enabled then init wasn't called so do that for the test. sitw.SignInToWebsiteUX.init(); } @@ -308,16 +310,18 @@ function resetState() { IdentityService.reset(); } // Cleanup after all tests function cleanUp() { info("cleanup"); resetState(); + PopupNotifications.transitionsEnabled = true; + for (let topic in gActiveObservers) Services.obs.removeObserver(gActiveObservers[topic], topic); for (let eventName in gActiveListeners) PopupNotifications.panel.removeEventListener(eventName, gActiveListeners[eventName], false); delete IdentityService.RP._rpFlows[outerWinId]; // Put the JSM functions back to how they were IdentityService.IDP.setAuthenticationFlow = window.setAuthenticationFlow;
--- a/browser/modules/test/browser_UITour3.js +++ b/browser/modules/test/browser_UITour3.js @@ -18,16 +18,20 @@ function test() { let tests = [ function test_info_icon(done) { let popup = document.getElementById("UITourTooltip"); let title = document.getElementById("UITourTooltipTitle"); let desc = document.getElementById("UITourTooltipDescription"); let icon = document.getElementById("UITourTooltipIcon"); let buttons = document.getElementById("UITourTooltipButtons"); + // Disable the animation to prevent the mouse clicks from hitting the main + // window during the transition instead of the buttons in the popup. + popup.setAttribute("animate", "false"); + popup.addEventListener("popupshown", function onPopupShown() { popup.removeEventListener("popupshown", onPopupShown); is(title.textContent, "a title", "Popup should have correct title"); is(desc.textContent, "some text", "Popup should have correct description text"); let imageURL = getRootDirectory(gTestPath) + "image.png"; imageURL = imageURL.replace("chrome://mochitests/content/", "https://example.com/"); @@ -145,16 +149,17 @@ let tests = [ function test_info_target_callback(done) { let popup = document.getElementById("UITourTooltip"); popup.addEventListener("popupshown", function onPopupShown() { popup.removeEventListener("popupshown", onPopupShown); PanelUI.show().then(() => { is(gContentWindow.callbackResult, "target", "target callback called"); is(gContentWindow.callbackData.target, "appMenu", "target callback was from the appMenu"); is(gContentWindow.callbackData.type, "popupshown", "target callback was from the mousedown"); + popup.removeAttribute("animate"); done(); }); }); let infoOptions = gContentWindow.makeInfoOptions(); gContentAPI.showInfo("appMenu", "I want to know when the target is clicked", "*click*", null, null, infoOptions); }, ];
--- a/configure.in +++ b/configure.in @@ -4890,17 +4890,17 @@ fi AC_SUBST(MOZ_ENABLE_LIBPROXY) AC_SUBST(MOZ_LIBPROXY_CFLAGS) AC_SUBST(MOZ_LIBPROXY_LIBS) dnl ======================================================== dnl = GNOME component (mozgnome) dnl ======================================================== -if test "$MOZ_ENABLE_GTK2" +if test "$MOZ_ENABLE_GTK" then MOZ_ENABLE_GNOME_COMPONENT=1 fi AC_SUBST(MOZ_ENABLE_GNOME_COMPONENT) dnl ======================================================== dnl = libgnomeui support module dnl ========================================================
--- a/content/base/src/nsXMLHttpRequest.cpp +++ b/content/base/src/nsXMLHttpRequest.cpp @@ -3659,27 +3659,17 @@ nsXMLHttpRequest::GetInterface(const nsI } return QueryInterface(aIID, aResult); } JS::Value nsXMLHttpRequest::GetInterface(JSContext* aCx, nsIJSID* aIID, ErrorResult& aRv) { - const nsID* iid = aIID->GetID(); - nsCOMPtr<nsISupports> result; - JS::Rooted<JS::Value> v(aCx, JSVAL_NULL); - aRv = GetInterface(*iid, getter_AddRefs(result)); - NS_ENSURE_FALSE(aRv.Failed(), JSVAL_NULL); - - JS::Rooted<JSObject*> wrapper(aCx, GetWrapper()); - JSAutoCompartment ac(aCx, wrapper); - JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForObject(aCx, wrapper)); - aRv = nsContentUtils::WrapNative(aCx, global, result, iid, &v); - return aRv.Failed() ? JSVAL_NULL : v; + return dom::GetInterface(aCx, this, aIID, aRv); } nsXMLHttpRequestUpload* nsXMLHttpRequest::Upload() { if (!mUpload) { mUpload = new nsXMLHttpRequestUpload(this); }
--- a/content/html/document/test/test_bug512367.html +++ b/content/html/document/test/test_bug512367.html @@ -20,18 +20,18 @@ https://bugzilla.mozilla.org/show_bug.cg <pre id="test"> <script type="application/javascript"> var frame = document.getElementById("i"); SimpleTest.waitForExplicitFinish(); addLoadEvent(function() { var viewer = - SpecialPowers.wrap(frame.contentWindow - .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)) + SpecialPowers.wrap(frame.contentWindow) + .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor) .getInterface(SpecialPowers.Ci.nsIWebNavigation) .QueryInterface(SpecialPowers.Ci.nsIDocShell) .contentViewer .QueryInterface(SpecialPowers.Ci.nsIMarkupDocumentViewer); viewer.fullZoom = 1.5; setTimeout(function() {
--- a/content/media/webspeech/synth/test/common.js +++ b/content/media/webspeech/synth/test/common.js @@ -1,10 +1,8 @@ -SpecialPowers.setBoolPref("media.webspeech.synth.enabled", true); - var gSpeechRegistry = SpecialPowers.Cc["@mozilla.org/synth-voice-registry;1"] .getService(SpecialPowers.Ci.nsISynthVoiceRegistry); var gAddedVoices = []; function SpeechTaskCallback(onpause, onresume, oncancel) { this.onpause = onpause; this.onresume = onresume; @@ -165,17 +163,16 @@ function synthCleanup() { is(voicesAfter, voicesBefore - toRemove, "Successfully removed test voices"); } else { // XXX: It would be nice to check here that the child gets the voice // removed update, but alas, it is aynchronous. var mm = SpecialPowers.Cc["@mozilla.org/childprocessmessagemanager;1"] .getService(SpecialPowers.Ci.nsISyncMessageSender); mm.sendSyncMessage('test:SpeechSynthesis:ipcSynthCleanup'); } - SpecialPowers.clearUserPref("media.webspeech.synth.enabled"); } function synthTestQueue(aTestArgs, aEndFunc) { var utterances = []; for (var i in aTestArgs) { var uargs = aTestArgs[i][0]; var u = new SpeechSynthesisUtterance(uargs.text);
copy from content/media/webspeech/synth/test/test_setup.html copy to content/media/webspeech/synth/test/file_setup.html --- a/content/media/webspeech/synth/test/test_setup.html +++ b/content/media/webspeech/synth/test/file_setup.html @@ -1,19 +1,23 @@ <!DOCTYPE HTML> <html> <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=525444 --> <head> <meta charset="utf-8"> <title>Test for Bug 525444: Web Speech API check all classes are present</title> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript"> + window.SimpleTest = parent.SimpleTest; + window.is = parent.is; + window.isnot = parent.isnot; + window.ok = parent.ok; + </script> <script type="application/javascript" src="common.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=650295">Mozilla Bug 650295</a> <p id="display"></p> <div id="content" style="display: none"> </div> <pre id="test"> @@ -67,12 +71,14 @@ var voices2 = speechSynthesis.getVoices( ok(voices1.length == voices2.length, "Voice count matches"); for (var i in voices1) { ok(voices1[i] == voices2[i], "Voice instance matches"); } synthCleanup(); + +SimpleTest.finish(); </script> </pre> </body> </html>
copy from content/media/webspeech/synth/test/test_speech_queue.html copy to content/media/webspeech/synth/test/file_speech_queue.html --- a/content/media/webspeech/synth/test/test_speech_queue.html +++ b/content/media/webspeech/synth/test/file_speech_queue.html @@ -1,33 +1,35 @@ <!DOCTYPE HTML> <html lang="en-US"> <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=525444 --> <head> <meta charset="utf-8"> <title>Test for Bug 525444: Web Speech API, check speech synth queue</title> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript"> + window.SimpleTest = parent.SimpleTest; + window.is = parent.is; + window.isnot = parent.isnot; + window.ok = parent.ok; + </script> <script type="application/javascript" src="common.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=525444">Mozilla Bug 525444</a> <p id="display"></p> <div id="content" style="display: none"> </div> <pre id="test"> <script type="application/javascript"> /** Test for Bug 525444 **/ -SimpleTest.waitForExplicitFinish(); - var englishJamaican = synthAddVoice('TestSpeechServiceNoAudio', 'Bob Marley', 'en-JM', true); var englishBritish = synthAddVoice('TestSpeechServiceNoAudio', 'Amy Winehouse', 'en-GB', true); var englishCanadian = synthAddVoice('TestSpeechServiceNoAudio', 'Leonard Cohen', 'en-CA', true); var frenchCanadian = synthAddVoice('TestSpeechServiceNoAudio', 'Celine Dion', 'fr-CA', true);
copy from content/media/webspeech/synth/test/test_speech_simple.html copy to content/media/webspeech/synth/test/file_speech_simple.html --- a/content/media/webspeech/synth/test/test_speech_simple.html +++ b/content/media/webspeech/synth/test/file_speech_simple.html @@ -1,33 +1,36 @@ <!DOCTYPE HTML> <html> <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=650295 --> <head> <meta charset="utf-8"> <title>Test for Bug 650295: Web Speech API check all classes are present</title> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript"> + window.SimpleTest = parent.SimpleTest; + window.info = parent.info; + window.is = parent.is; + window.isnot = parent.isnot; + window.ok = parent.ok; + </script> <script type="application/javascript" src="common.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=650295">Mozilla Bug 650295</a> <p id="display"></p> <div id="content" style="display: none"> </div> <pre id="test"> <script type="application/javascript"> /** Test for Bug 525444 **/ -SimpleTest.waitForExplicitFinish(); - synthAddVoice('TestSpeechServiceWithAudio', 'Male 1', 'en-GB', true); var gotStartEvent = false; var gotBoundaryEvent = false; var utterance = new SpeechSynthesisUtterance("Hello, world!"); utterance.addEventListener('start', function(e) { ok(speechSynthesis.speaking, "speechSynthesis is speaking."); ok(!speechSynthesis.pending, "speechSynthesis has no other utterances queued.");
--- a/content/media/webspeech/synth/test/mochitest.ini +++ b/content/media/webspeech/synth/test/mochitest.ini @@ -1,9 +1,13 @@ [DEFAULT] skip-if = e10s -support-files = common.js +support-files = + common.js + file_setup.html + file_speech_queue.html + file_speech_simple.html [test_setup.html] [test_speech_queue.html] skip-if = buildapp == 'b2g' # b2g(Test timed out) b2g-debug(Test timed out) b2g-desktop(Test timed out) [test_speech_simple.html] skip-if = buildapp == 'b2g' # b2g(Test timed out) b2g-debug(Test timed out) b2g-desktop(Test timed out)
--- a/content/media/webspeech/synth/test/test_setup.html +++ b/content/media/webspeech/synth/test/test_setup.html @@ -2,77 +2,31 @@ <html> <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=525444 --> <head> <meta charset="utf-8"> <title>Test for Bug 525444: Web Speech API check all classes are present</title> <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <script type="application/javascript" src="common.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=650295">Mozilla Bug 650295</a> <p id="display"></p> +<iframe id="testFrame"></iframe> <div id="content" style="display: none"> </div> <pre id="test"> <script type="application/javascript"> /** Test for Bug 525444 **/ -synthAddVoice('TestSpeechServiceNoAudio', 'Bob Marley', 'en-JM', true); -synthAddVoice('TestSpeechServiceNoAudio', 'Amy Winehouse', 'en-GB', true); -synthAddVoice('TestSpeechServiceNoAudio', 'Leonard Cohen', 'en-CA', true); -synthAddVoice('TestSpeechServiceNoAudio', 'Celine Dion', 'fr-CA', true); -synthAddVoice('TestSpeechServiceNoAudio', 'Julieta Venegas', 'es-MX', true); - -ok(SpeechSynthesis, "SpeechSynthesis exists in global scope"); -ok(SpeechSynthesisVoice, "SpeechSynthesisVoice exists in global scope"); -ok(SpeechSynthesisEvent, "SpeechSynthesisEvent exists in global scope"); +SimpleTest.waitForExplicitFinish(); -// SpeechSynthesisUtterance is the only type that has a constructor -// and writable properties -ok(SpeechSynthesisUtterance, "SpeechSynthesisUtterance exists in global scope"); -var ssu = new SpeechSynthesisUtterance("hello world"); -is(typeof ssu, "object", "SpeechSynthesisUtterance instance is an object"); -is(ssu.text, "hello world", "SpeechSynthesisUtterance.text is correct"); -is(ssu.volume, 1, "SpeechSynthesisUtterance.volume default is correct"); -is(ssu.rate, 1, "SpeechSynthesisUtterance.rate default is correct"); -is(ssu.pitch, 1, "SpeechSynthesisUtterance.pitch default is correct"); -ssu.lang = "he-IL"; -ssu.volume = 0.5; -ssu.rate = 2.0; -ssu.pitch = 1.5; -is(ssu.lang, "he-IL", "SpeechSynthesisUtterance.lang is correct"); -is(ssu.volume, 0.5, "SpeechSynthesisUtterance.volume is correct"); -is(ssu.rate, 2.0, "SpeechSynthesisUtterance.rate is correct"); -is(ssu.pitch, 1.5, "SpeechSynthesisUtterance.pitch is correct"); +SpecialPowers.pushPrefEnv({ set: [['media.webspeech.synth.enabled', true]] }, + function() { document.getElementById("testFrame").src = "file_setup.html"; }); -// Test for singleton instance hanging off of window. -ok(speechSynthesis, "speechSynthesis exists in global scope"); -is(typeof speechSynthesis, "object", "speechSynthesis instance is an object"); -is(typeof speechSynthesis.speak, "function", "speechSynthesis.speak is a function"); -is(typeof speechSynthesis.cancel, "function", "speechSynthesis.cancel is a function"); -is(typeof speechSynthesis.pause, "function", "speechSynthesis.pause is a function"); -is(typeof speechSynthesis.resume, "function", "speechSynthesis.resume is a function"); -is(typeof speechSynthesis.getVoices, "function", "speechSynthesis.getVoices is a function"); - -is(typeof speechSynthesis.pending, "boolean", "speechSynthesis.pending is a boolean"); -is(typeof speechSynthesis.speaking, "boolean", "speechSynthesis.speaking is a boolean"); -is(typeof speechSynthesis.paused, "boolean", "speechSynthesis.paused is a boolean"); - -var voices1 = speechSynthesis.getVoices(); -var voices2 = speechSynthesis.getVoices(); - -ok(voices1.length == voices2.length, "Voice count matches"); - -for (var i in voices1) { - ok(voices1[i] == voices2[i], "Voice instance matches"); -} - -synthCleanup(); </script> </pre> </body> </html>
--- a/content/media/webspeech/synth/test/test_speech_queue.html +++ b/content/media/webspeech/synth/test/test_speech_queue.html @@ -8,63 +8,26 @@ https://bugzilla.mozilla.org/show_bug.cg <title>Test for Bug 525444: Web Speech API, check speech synth queue</title> <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <script type="application/javascript" src="common.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=525444">Mozilla Bug 525444</a> <p id="display"></p> +<iframe id="testFrame"></iframe> <div id="content" style="display: none"> </div> <pre id="test"> <script type="application/javascript"> /** Test for Bug 525444 **/ SimpleTest.waitForExplicitFinish(); -var englishJamaican = synthAddVoice('TestSpeechServiceNoAudio', - 'Bob Marley', 'en-JM', true); -var englishBritish = synthAddVoice('TestSpeechServiceNoAudio', - 'Amy Winehouse', 'en-GB', true); -var englishCanadian = synthAddVoice('TestSpeechServiceNoAudio', - 'Leonard Cohen', 'en-CA', true); -var frenchCanadian = synthAddVoice('TestSpeechServiceNoAudio', - 'Celine Dion', 'fr-CA', true); -var spanishMexican = synthAddVoice('TestSpeechServiceNoAudio', - 'Julieta Venegas', 'es-MX', true); - -synthSetDefault(englishBritish, true); - -synthTestQueue( - [[{text: "Hello, world."}, - { uri: englishBritish }], - [{text: "Bonjour tout le monde .", lang: "fr", rate: 0.5, pitch: 0.75}, - { uri: frenchCanadian, rate: 0.5, pitch: 0.75}], - [{text: "How are you doing?", lang: "en-GB"}, - { rate: 1, pitch: 1, uri: englishBritish}], - [{text: "¡hasta mañana", lang: "es-ES"}, - { uri: spanishMexican }]], - function () { - synthSetDefault(englishJamaican, true); - var test_data = [[{text: "I shot the sheriff."}, - { uri: englishJamaican }]]; - var voices = speechSynthesis.getVoices(); - for (var i in voices) { - test_data.push([{text: "Hello world", voice: voices[i]}, - {uri: voices[i].voiceURI}]); - } - - synthTestQueue(test_data, - function () { - synthCleanup(); - SimpleTest.finish(); - }); - }); - - +SpecialPowers.pushPrefEnv({ set: [['media.webspeech.synth.enabled', true]] }, + function() { document.getElementById("testFrame").src = "file_speech_queue.html"; }); </script> </pre> </body> </html>
--- a/content/media/webspeech/synth/test/test_speech_simple.html +++ b/content/media/webspeech/synth/test/test_speech_simple.html @@ -8,46 +8,26 @@ https://bugzilla.mozilla.org/show_bug.cg <title>Test for Bug 650295: Web Speech API check all classes are present</title> <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <script type="application/javascript" src="common.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=650295">Mozilla Bug 650295</a> <p id="display"></p> +<iframe id="testFrame"></iframe> <div id="content" style="display: none"> </div> <pre id="test"> <script type="application/javascript"> /** Test for Bug 525444 **/ SimpleTest.waitForExplicitFinish(); -synthAddVoice('TestSpeechServiceWithAudio', 'Male 1', 'en-GB', true); - -var gotStartEvent = false; -var gotBoundaryEvent = false; -var utterance = new SpeechSynthesisUtterance("Hello, world!"); -utterance.addEventListener('start', function(e) { - ok(speechSynthesis.speaking, "speechSynthesis is speaking."); - ok(!speechSynthesis.pending, "speechSynthesis has no other utterances queued."); - gotStartEvent = true; -}); - -utterance.addEventListener('end', function(e) { - ok(!speechSynthesis.speaking, "speechSynthesis is not speaking."); - ok(!speechSynthesis.pending, "speechSynthesis has no other utterances queued."); - ok(gotStartEvent, "Got 'start' event."); - info('end ' + e.elapsedTime); - synthCleanup(); - SimpleTest.finish(); -}); - -speechSynthesis.speak(utterance); -ok(!speechSynthesis.speaking, "speechSynthesis is not speaking yet."); -ok(speechSynthesis.pending, "speechSynthesis has an utterance queued."); +SpecialPowers.pushPrefEnv({ set: [['media.webspeech.synth.enabled', true]] }, + function() { document.getElementById("testFrame").src = "file_speech_simple.html"; }); </script> </pre> </body> </html>
--- a/content/xul/content/src/nsXULPopupListener.cpp +++ b/content/xul/content/src/nsXULPopupListener.cpp @@ -292,17 +292,17 @@ void nsXULPopupListener::ClosePopup() { if (mPopupContent) { // this is called when the listener is going away, so make sure that the // popup is hidden. Use asynchronous hiding just to be safe so we don't // fire events during destruction. nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); if (pm) - pm->HidePopup(mPopupContent, false, true, true); + pm->HidePopup(mPopupContent, false, true, true, false); mPopupContent = nullptr; // release the popup } } // ClosePopup static already_AddRefed<nsIContent> GetImmediateChild(nsIContent* aContent, nsIAtom *aTag) { for (nsIContent* child = aContent->GetFirstChild();
--- a/dom/apps/src/OperatorApps.jsm +++ b/dom/apps/src/OperatorApps.jsm @@ -137,17 +137,18 @@ this.OperatorAppsRegistry = { _copyDirectory: function(aOrg, aDst) { debug("copying " + aOrg + " to " + aDst); return aDst && Task.spawn(function() { try { let orgDir = Cc["@mozilla.org/file/local;1"] .createInstance(Ci.nsIFile); orgDir.initWithPath(aOrg); - if (!orgDir.isDirectory()) { + if (!orgDir.exists() || !orgDir.isDirectory()) { + debug(aOrg + " does not exist or is not a directory"); return; } let dstDir = Cc["@mozilla.org/file/local;1"] .createInstance(Ci.nsIFile); dstDir.initWithPath(aDst); if (!dstDir.exists()) { dstDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY); @@ -163,17 +164,17 @@ this.OperatorAppsRegistry = { dstFile.append(entry.leafName); if(dstFile.exists()) { dstFile.remove(false); } entry.copyTo(dstDir, entry.leafName); } else { yield this._copyDirectory(entry.path, - Path.join(aDst, entry.name)); + Path.join(aDst, entry.leafName)); } } } catch (e) { debug("Error copying " + aOrg + " to " + aDst + ". " + e); } }.bind(this)); },
--- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -177,17 +177,16 @@ using namespace mozilla::dom; static NS_DEFINE_CID(kDOMSOF_CID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID); // NOTE: DEFAULT_SCRIPTABLE_FLAGS and DOM_DEFAULT_SCRIPTABLE_FLAGS // are defined in nsIDOMClassInfo.h. #define WINDOW_SCRIPTABLE_FLAGS \ (nsIXPCScriptable::WANT_PRECREATE | \ nsIXPCScriptable::WANT_POSTCREATE | \ - nsIXPCScriptable::WANT_FINALIZE | \ nsIXPCScriptable::WANT_ENUMERATE | \ nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE | \ nsIXPCScriptable::IS_GLOBAL_OBJECT | \ nsIXPCScriptable::WANT_OUTER_OBJECT) #define ARRAY_SCRIPTABLE_FLAGS \ (DOM_DEFAULT_SCRIPTABLE_FLAGS | \ nsIXPCScriptable::WANT_GETPROPERTY | \ @@ -1850,17 +1849,17 @@ NS_IMETHODIMP nsWindowSH::PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj) { JS::Rooted<JSObject*> window(cx, obj); #ifdef DEBUG nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryWrappedNative(wrapper)); - NS_ASSERTION(sgo && sgo->GetGlobalJSObject() == nullptr, + NS_ASSERTION(sgo && sgo->GetGlobalJSObject() == obj, "Multiple wrappers created for global object!"); #endif const NativeProperties* windowProperties = WindowBinding::sNativePropertyHooks->mNativeProperties.regular; const NativeProperties* eventTargetProperties = EventTargetBinding::sNativePropertyHooks->mNativeProperties.regular; @@ -3395,36 +3394,16 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp return NS_OK; } return nsDOMGenericSH::NewResolve(wrapper, cx, obj, id, flags, objp, _retval); } NS_IMETHODIMP -nsWindowSH::Finalize(nsIXPConnectWrappedNative *wrapper, JSFreeOp *fop, - JSObject *obj) -{ - // Since this call is virtual, the exact rooting hazard static analysis is - // not able to determine that it happens during finalization and should be - // ignored. Moreover, the analysis cannot discover and validate the - // potential targets of the virtual call to OnFinalize below because of the - // indirection through nsCOMMPtr. Thus, we annotate the analysis here so - // that it does not report OnFinalize as GCing with |obj| on stack. - JS::AutoAssertNoGC nogc; - - nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryWrappedNative(wrapper)); - NS_ENSURE_TRUE(sgo, NS_ERROR_UNEXPECTED); - - sgo->OnFinalize(obj); - - return NS_OK; -} - -NS_IMETHODIMP nsWindowSH::OuterObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx, JSObject * obj, JSObject * *_retval) { nsGlobalWindow *origWin = nsGlobalWindow::FromWrapper(wrapper); nsGlobalWindow *win = origWin->GetOuterWindowInternal(); if (!win) { // If we no longer have an outer window. No code should ever be
--- a/dom/base/nsDOMClassInfo.h +++ b/dom/base/nsDOMClassInfo.h @@ -265,18 +265,16 @@ public: NS_IMETHOD PostCreatePrototype(JSContext * cx, JSObject * proto) MOZ_OVERRIDE; NS_IMETHOD PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj) MOZ_OVERRIDE; NS_IMETHOD Enumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, bool *_retval) MOZ_OVERRIDE; NS_IMETHOD NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, jsid id, uint32_t flags, JSObject **objp, bool *_retval) MOZ_OVERRIDE; - NS_IMETHOD Finalize(nsIXPConnectWrappedNative *wrapper, JSFreeOp *fop, - JSObject *obj) MOZ_OVERRIDE; NS_IMETHOD OuterObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx, JSObject * obj, JSObject * *_retval) MOZ_OVERRIDE; static bool GlobalScopePolluterNewResolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, unsigned flags, JS::MutableHandle<JSObject*> objp); static bool GlobalScopePolluterGetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp);
--- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -1366,17 +1366,16 @@ nsGlobalWindow::ShutDown() // static void nsGlobalWindow::CleanupCachedXBLHandlers(nsGlobalWindow* aWindow) { if (aWindow->mCachedXBLPrototypeHandlers && aWindow->mCachedXBLPrototypeHandlers->Count() > 0) { aWindow->mCachedXBLPrototypeHandlers->Clear(); - mozilla::DropJSObjects(aWindow); } } void nsGlobalWindow::MaybeForgiveSpamCount() { if (IsOuterWindow() && IsPopupSpamWindow()) @@ -1612,27 +1611,21 @@ nsGlobalWindow::FreeInnerObjects() mGamepads.Clear(); #endif } //***************************************************************************** // nsGlobalWindow::nsISupports //***************************************************************************** -#define OUTER_WINDOW_ONLY \ - if (IsOuterWindow()) { - -#define END_OUTER_WINDOW_ONLY \ - foundInterface = 0; \ - } else - DOMCI_DATA(Window, nsGlobalWindow) // QueryInterface implementation for nsGlobalWindow NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGlobalWindow) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY // Make sure this matches the cast in nsGlobalWindow::FromWrapper() NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEventTarget) NS_INTERFACE_MAP_ENTRY(nsIDOMWindow) #ifdef MOZ_B2G NS_INTERFACE_MAP_ENTRY(nsIDOMWindowB2G) #endif // MOZ_B2G #ifdef MOZ_WEBSPEECH NS_INTERFACE_MAP_ENTRY(nsISpeechSynthesisGetter) @@ -1655,19 +1648,16 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION( NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget) NS_INTERFACE_MAP_ENTRY(nsPIDOMWindow) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) NS_INTERFACE_MAP_ENTRY(nsIDOMWindowPerformance) NS_INTERFACE_MAP_ENTRY(nsITouchEventReceiver) NS_INTERFACE_MAP_ENTRY(nsIInlineEventHandlers) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Window) - OUTER_WINDOW_ONLY - NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY - END_OUTER_WINDOW_ONLY NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindow) NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindow) static PLDHashOperator MarkXBLHandlers(nsXBLPrototypeHandler* aKey, JS::Heap<JSObject*>& aData, void* aClosure) @@ -1717,16 +1707,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_ NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGlobalWindow, tmp->mRefCnt.get()) } NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArguments) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDialogArguments) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReturnValue) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance) #ifdef MOZ_WEBSPEECH NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis) #endif @@ -1764,26 +1755,28 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mToolbar) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocationbar) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPersonalbar) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStatusbar) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollbars) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExternal) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow) nsGlobalWindow::CleanupCachedXBLHandlers(tmp); NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext) NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers) NS_IMPL_CYCLE_COLLECTION_UNLINK(mArguments) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDialogArguments) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mReturnValue) NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator) NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance) #ifdef MOZ_WEBSPEECH NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechSynthesis) #endif @@ -1823,16 +1816,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns NS_IMPL_CYCLE_COLLECTION_UNLINK(mToolbar) NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocationbar) NS_IMPL_CYCLE_COLLECTION_UNLINK(mPersonalbar) NS_IMPL_CYCLE_COLLECTION_UNLINK(mStatusbar) NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollbars) NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto) NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole) NS_IMPL_CYCLE_COLLECTION_UNLINK(mExternal) + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER NS_IMPL_CYCLE_COLLECTION_UNLINK_END #ifdef DEBUG void nsGlobalWindow::RiskyUnlink() { NS_CYCLE_COLLECTION_INNERNAME.Unlink(this); } @@ -1852,16 +1846,17 @@ TraceXBLHandlers(nsXBLPrototypeHandler* return PL_DHASH_NEXT; } NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindow) if (tmp->mCachedXBLPrototypeHandlers) { TraceData data = { aCallbacks, aClosure }; tmp->mCachedXBLPrototypeHandlers->Enumerate(TraceXBLHandlers, &data); } + NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER NS_IMPL_CYCLE_COLLECTION_TRACE_END bool nsGlobalWindow::IsBlackForCC(bool aTracingNeeded) { if (!nsCCUncollectableMarker::sGeneration) { return false; } @@ -1897,22 +1892,22 @@ nsresult nsGlobalWindow::EnsureScriptEnvironment() { nsGlobalWindow* outer = GetOuterWindowInternal(); if (!outer) { NS_WARNING("No outer window available!"); return NS_ERROR_FAILURE; } - if (outer->mJSObject) { + if (outer->GetWrapperPreserveColor()) { return NS_OK; } NS_ASSERTION(!outer->GetCurrentInnerWindowInternal(), - "mJSObject is null, but we have an inner window?"); + "No cached wrapper, but we have an inner window?"); // If this window is a [i]frame, don't bother GC'ing when the frame's context // is destroyed since a GC will happen when the frameset or host document is // destroyed anyway. nsCOMPtr<nsIScriptContext> context = new nsJSContext(!IsFrame(), outer); NS_ASSERTION(!outer->mContext, "Will overwrite mContext!"); @@ -1937,19 +1932,50 @@ JSObject * nsGlobalWindow::GetGlobalJSObject() { return FastGetGlobalJSObject(); } void nsGlobalWindow::TraceGlobalJSObject(JSTracer* aTrc) { - if (mJSObject) { - JS_CallTenuredObjectTracer(aTrc, &mJSObject, "active window global"); - } + TraceWrapper(aTrc, "active window global"); +} + +/* static */ +JSObject* +nsGlobalWindow::OuterObject(JSContext* aCx, JS::HandleObject aObj) +{ + nsGlobalWindow *origWin; + UNWRAP_OBJECT(Window, aObj, origWin); + nsGlobalWindow *win = origWin->GetOuterWindowInternal(); + + if (!win) { + // If we no longer have an outer window. No code should ever be + // running on a window w/o an outer, which means this hook should + // never be called when we have no outer. But just in case, return + // null to prevent leaking an inner window to code in a different + // window. + NS_WARNING("nsGlobalWindow::OuterObject shouldn't fail!"); + return nullptr; + } + + JS::Rooted<JSObject*> winObj(aCx, win->FastGetGlobalJSObject()); + MOZ_ASSERT(winObj); + + // Note that while |wrapper| is same-compartment with cx, the outer window + // might not be. If we're running script in an inactive scope and evalute + // |this|, the outer window is actually a cross-compartment wrapper. So we + // need to wrap here. + if (!JS_WrapObject(aCx, &winObj)) { + NS_WARNING("nsGlobalWindow::OuterObject shouldn't fail!"); + return nullptr; + } + + return winObj; } bool nsGlobalWindow::WouldReuseInnerWindow(nsIDocument *aNewDocument) { // We reuse the inner window when: // a. We are currently at our original document. // b. At least one of the following conditions are true: @@ -2122,17 +2148,17 @@ WindowStateHolder::WindowStateHolder(nsG NS_PRECONDITION(aWindow, "null window"); NS_PRECONDITION(aWindow->IsInnerWindow(), "Saving an outer window"); mInnerWindowHolder = aHolder; aWindow->SuspendTimeouts(); // When a global goes into the bfcache, we disable script. - xpc::Scriptability::Get(aWindow->mJSObject).SetDocShellAllowsScript(false); + xpc::Scriptability::Get(aWindow->GetWrapperPreserveColor()).SetDocShellAllowsScript(false); } WindowStateHolder::~WindowStateHolder() { if (mInnerWindow) { // This window was left in the bfcache and is now going away. We need to // free it up. // Note that FreeInnerObjects may already have been called on the @@ -2160,17 +2186,16 @@ TreatAsRemoteXUL(nsIPrincipal* aPrincipa * Return the native global and an nsISupports 'holder' that can be used * to manage the lifetime of it. */ static nsresult CreateNativeGlobalForInner(JSContext* aCx, nsGlobalWindow* aNewInner, nsIURI* aURI, nsIPrincipal* aPrincipal, - JS::TenuredHeap<JSObject*>& aNativeGlobal, nsIXPConnectJSObjectHolder** aHolder) { MOZ_ASSERT(aCx); MOZ_ASSERT(aNewInner); MOZ_ASSERT(aNewInner->IsInnerWindow()); MOZ_ASSERT(aPrincipal); MOZ_ASSERT(aHolder); @@ -2188,30 +2213,25 @@ CreateNativeGlobalForInner(JSContext* aC nsIXPConnect* xpc = nsContentUtils::XPConnect(); // Determine if we need the Components object. bool needComponents = nsContentUtils::IsSystemPrincipal(aPrincipal) || TreatAsRemoteXUL(aPrincipal); uint32_t flags = needComponents ? 0 : nsIXPConnect::OMIT_COMPONENTS_OBJECT; flags |= nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK; - nsRefPtr<nsIXPConnectJSObjectHolder> jsholder; nsresult rv = xpc->InitClassesWithNewWrappedGlobal( aCx, ToSupports(aNewInner), - aPrincipal, flags, options, getter_AddRefs(jsholder)); + aPrincipal, flags, options, aHolder); NS_ENSURE_SUCCESS(rv, rv); - MOZ_ASSERT(jsholder); - aNativeGlobal = jsholder->GetJSObject(); - jsholder.forget(aHolder); - // Set the location information for the new global, so that tools like // about:memory may use that information - MOZ_ASSERT(aNativeGlobal.getPtr()); - xpc::SetLocationForGlobal(aNativeGlobal, aURI); + MOZ_ASSERT(aNewInner->GetWrapperPreserveColor()); + xpc::SetLocationForGlobal(aNewInner->GetWrapperPreserveColor(), aURI); return NS_OK; } nsresult nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, nsISupports* aState, bool aForceReuseInnerWindow) @@ -2331,66 +2351,66 @@ nsGlobalWindow::SetNewDocument(nsIDocume // Check if we're near the stack limit before we get anywhere near the // transplanting code. JS_CHECK_RECURSION(cx, return NS_ERROR_FAILURE); nsCOMPtr<WindowStateHolder> wsh = do_QueryInterface(aState); NS_ASSERTION(!aState || wsh, "What kind of weird state are you giving me here?"); + JS::Rooted<JSObject*> newInnerGlobal(cx); if (reUseInnerWindow) { // We're reusing the current inner window. NS_ASSERTION(!currentInner->IsFrozen(), "We should never be reusing a shared inner window"); newInnerWindow = currentInner; + newInnerGlobal = currentInner->GetWrapperPreserveColor(); if (aDocument != oldDoc) { - JS::Rooted<JSObject*> obj(cx, currentInner->mJSObject); - JS::ExposeObjectToActiveJS(obj); + JS::ExposeObjectToActiveJS(newInnerGlobal); } // We're reusing the inner window, but this still counts as a navigation, // so all expandos and such defined on the outer window should go away. Force // all Xray wrappers to be recomputed. - JS::ExposeObjectToActiveJS(mJSObject); - JS::Rooted<JSObject*> rootedObject(cx, mJSObject); + JS::Rooted<JSObject*> rootedObject(cx, GetWrapperPreserveColor()); + JS::ExposeObjectToActiveJS(rootedObject); if (!JS_RefreshCrossCompartmentWrappers(cx, rootedObject)) { return NS_ERROR_FAILURE; } // Inner windows are only reused for same-origin principals, but the principals // don't necessarily match exactly. Update the principal on the compartment to // match the new document. // NB: We don't just call currentInner->RefreshCompartmentPrincipals() here // because we haven't yet set its mDoc to aDocument. - JSCompartment *compartment = js::GetObjectCompartment(currentInner->mJSObject); + JSCompartment *compartment = js::GetObjectCompartment(newInnerGlobal); #ifdef DEBUG bool sameOrigin = false; nsIPrincipal *existing = nsJSPrincipals::get(JS_GetCompartmentPrincipals(compartment)); aDocument->NodePrincipal()->Equals(existing, &sameOrigin); MOZ_ASSERT(sameOrigin); #endif JS_SetCompartmentPrincipals(compartment, nsJSPrincipals::get(aDocument->NodePrincipal())); } else { if (aState) { newInnerWindow = wsh->GetInnerWindow(); + newInnerGlobal = newInnerWindow->GetWrapperPreserveColor(); mInnerWindowHolder = wsh->GetInnerWindowHolder(); - - NS_ASSERTION(newInnerWindow, "Got a state without inner window"); - } else if (thisChrome) { - newInnerWindow = new nsGlobalChromeWindow(this); - } else if (mIsModalContentWindow) { - newInnerWindow = new nsGlobalModalWindow(this); } else { - newInnerWindow = new nsGlobalWindow(this); - } - - if (!aState) { + if (thisChrome) { + newInnerWindow = new nsGlobalChromeWindow(this); + } else if (mIsModalContentWindow) { + newInnerWindow = new nsGlobalModalWindow(this); + } else { + newInnerWindow = new nsGlobalWindow(this); + } + // Freeze the outer window and null out the inner window so // that initializing classes on the new inner doesn't end up // reaching into the old inner window for classes etc. // // [This happens with Object.prototype when XPConnect creates // a temporary global while initializing classes; the reason // being that xpconnect creates the temp global w/o a parent // and proto, which makes the JS engine look up classes in @@ -2400,29 +2420,30 @@ nsGlobalWindow::SetNewDocument(nsIDocume Freeze(); mCreatingInnerWindow = true; // Every script context we are initialized with must create a // new global. rv = CreateNativeGlobalForInner(cx, newInnerWindow, aDocument->GetDocumentURI(), aDocument->NodePrincipal(), - newInnerWindow->mJSObject, getter_AddRefs(mInnerWindowHolder)); - NS_ASSERTION(NS_SUCCEEDED(rv) && newInnerWindow->mJSObject && mInnerWindowHolder, - "Failed to get script global and holder"); + newInnerGlobal = mInnerWindowHolder->GetJSObject(); + NS_ASSERTION(NS_SUCCEEDED(rv) && newInnerGlobal && + newInnerWindow->GetWrapperPreserveColor() == newInnerGlobal, + "Failed to get script global"); mCreatingInnerWindow = false; createdInnerWindow = true; Thaw(); NS_ENSURE_SUCCESS(rv, rv); } - if (currentInner && currentInner->mJSObject) { + if (currentInner && currentInner->GetWrapperPreserveColor()) { if (oldDoc == aDocument) { // Move the navigator from the old inner window to the new one since // this is a document.write. This is safe from a same-origin point of // view because document.write can only be used by the same origin. newInnerWindow->mNavigator = currentInner->mNavigator; currentInner->mNavigator = nullptr; if (newInnerWindow->mNavigator) { newInnerWindow->mNavigator->SetWindow(newInnerWindow); @@ -2444,122 +2465,114 @@ nsGlobalWindow::SetNewDocument(nsIDocume // held in the bfcache. if (!currentInner->IsFrozen()) { currentInner->FreeInnerObjects(); } } mInnerWindow = newInnerWindow; - if (!mJSObject) { - JS::Rooted<JSObject*> global(cx, newInnerWindow->FastGetGlobalJSObject()); - JS::Rooted<JSObject*> outer(cx, NewOuterWindowProxy(cx, global, thisChrome)); + if (!GetWrapperPreserveColor()) { + JS::Rooted<JSObject*> outer(cx, + NewOuterWindowProxy(cx, newInnerGlobal, thisChrome)); NS_ENSURE_TRUE(outer, NS_ERROR_FAILURE); js::SetProxyExtra(outer, 0, js::PrivateValue(ToSupports(this))); + js::SetProxyExtra(outer, 1, JS::ObjectValue(*newInnerGlobal)); // Inform the nsJSContext, which is the canonical holder of the outer. mContext->SetWindowProxy(outer); mContext->DidInitializeContext(); - mJSObject = mContext->GetWindowProxy(); - SetWrapper(mJSObject); + SetWrapper(mContext->GetWindowProxy()); } else { - JS::ExposeObjectToActiveJS(newInnerWindow->mJSObject); - JS::Rooted<JSObject*> global(cx, newInnerWindow->mJSObject); + JS::ExposeObjectToActiveJS(newInnerGlobal); JS::Rooted<JSObject*> outerObject(cx, - NewOuterWindowProxy(cx, global, thisChrome)); + NewOuterWindowProxy(cx, newInnerGlobal, thisChrome)); if (!outerObject) { NS_ERROR("out of memory"); return NS_ERROR_FAILURE; } - js::SetProxyExtra(mJSObject, 0, js::PrivateValue(nullptr)); - - JS::Rooted<JSObject*> obj(cx, mJSObject); + JS::Rooted<JSObject*> obj(cx, GetWrapperPreserveColor()); + + js::SetProxyExtra(obj, 0, js::PrivateValue(nullptr)); + outerObject = xpc::TransplantObject(cx, obj, outerObject); if (!outerObject) { NS_ERROR("unable to transplant wrappers, probably OOM"); return NS_ERROR_FAILURE; } js::SetProxyExtra(outerObject, 0, js::PrivateValue(ToSupports(this))); - mJSObject = outerObject; - SetWrapper(mJSObject); + SetWrapper(outerObject); { - JSAutoCompartment ac(cx, mJSObject); - - JS::Rooted<JSObject*> obj(cx, mJSObject); - JS::Rooted<JSObject*> newParent(cx, newInnerWindow->mJSObject); - JS_SetParent(cx, obj, newParent); + JSAutoCompartment ac(cx, outerObject); + + JS_SetParent(cx, outerObject, newInnerGlobal); // Inform the nsJSContext, which is the canonical holder of the outer. - mContext->SetWindowProxy(obj); + mContext->SetWindowProxy(outerObject); NS_ASSERTION(!JS_IsExceptionPending(cx), "We might overwrite a pending exception!"); - XPCWrappedNativeScope* scope = xpc::GetObjectScope(obj); + XPCWrappedNativeScope* scope = xpc::GetObjectScope(outerObject); if (scope->mWaiverWrapperMap) { - scope->mWaiverWrapperMap->Reparent(cx, newParent); + scope->mWaiverWrapperMap->Reparent(cx, newInnerGlobal); } } } // Enter the new global's compartment. - JSAutoCompartment ac(cx, mJSObject); + JSAutoCompartment ac(cx, GetWrapperPreserveColor()); // Set scriptability based on the state of the docshell. bool allow = GetDocShell()->GetCanExecuteScripts(); - xpc::Scriptability::Get(mJSObject).SetDocShellAllowsScript(allow); + xpc::Scriptability::Get(GetWrapperPreserveColor()).SetDocShellAllowsScript(allow); // If we created a new inner window above, we need to do the last little bit // of initialization now that the dust has settled. if (createdInnerWindow) { nsIXPConnect *xpc = nsContentUtils::XPConnect(); nsCOMPtr<nsIXPConnectWrappedNative> wrapper; - nsresult rv = xpc->GetWrappedNativeOfJSObject(cx, newInnerWindow->mJSObject, + nsresult rv = xpc->GetWrappedNativeOfJSObject(cx, newInnerGlobal, getter_AddRefs(wrapper)); NS_ENSURE_SUCCESS(rv, rv); NS_ABORT_IF_FALSE(wrapper, "bad wrapper"); rv = wrapper->FinishInitForWrappedGlobal(); NS_ENSURE_SUCCESS(rv, rv); } if (!aState) { - if (!JS_DefineProperty(cx, newInnerWindow->mJSObject, "window", - OBJECT_TO_JSVAL(mJSObject), + if (!JS_DefineProperty(cx, newInnerGlobal, "window", + OBJECT_TO_JSVAL(GetWrapperPreserveColor()), JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) { NS_ERROR("can't create the 'window' property"); return NS_ERROR_FAILURE; } } } - JSAutoCompartment ac(cx, mJSObject); + JSAutoCompartment ac(cx, GetWrapperPreserveColor()); if (!aState && !reUseInnerWindow) { // Loading a new page and creating a new inner window, *not* // restoring from session history. // Now that both the the inner and outer windows are initialized // let the script context do its magic to hook them together. + MOZ_ASSERT(mContext->GetWindowProxy() == GetWrapperPreserveColor()); #ifdef DEBUG - JS::Rooted<JSObject*> newInnerJSObject(cx, - newInnerWindow->FastGetGlobalJSObject()); -#endif - - MOZ_ASSERT(mContext->GetWindowProxy() == mJSObject); -#ifdef DEBUG - JS::Rooted<JSObject*> rootedJSObject(cx, mJSObject); + JS::Rooted<JSObject*> rootedJSObject(cx, GetWrapperPreserveColor()); JS::Rooted<JSObject*> proto1(cx), proto2(cx); JS_GetPrototype(cx, rootedJSObject, &proto1); - JS_GetPrototype(cx, newInnerJSObject, &proto2); + JS_GetPrototype(cx, newInnerGlobal, &proto2); NS_ASSERTION(proto1 == proto2, "outer and inner globals should have the same prototype"); #endif nsCOMPtr<Element> frame = GetFrameElementInternal(); if (frame) { nsPIDOMWindow* parentWindow = frame->OwnerDoc()->GetWindow(); if (parentWindow && parentWindow->TimeoutSuspendCount()) { @@ -2578,34 +2591,34 @@ nsGlobalWindow::SetNewDocument(nsIDocume if (newInnerWindow->mDoc != aDocument) { newInnerWindow->mDoc = aDocument; // We're reusing the inner window for a new document. In this // case we don't clear the inner window's scope, but we must // make sure the cached document property gets updated. // XXXmarkh - tell other languages about this? - JS::Rooted<JSObject*> obj(cx, currentInner->mJSObject); + JS::Rooted<JSObject*> obj(cx, currentInner->GetWrapperPreserveColor()); ::JS_DeleteProperty(cx, obj, "document"); } } else { newInnerWindow->InnerSetNewDocument(aDocument); // Initialize DOM classes etc on the inner window. - JS::Rooted<JSObject*> obj(cx, newInnerWindow->mJSObject); + JS::Rooted<JSObject*> obj(cx, newInnerGlobal); rv = mContext->InitClasses(obj); NS_ENSURE_SUCCESS(rv, rv); } // If the document comes from a JAR, check if the channel was determined // to be unsafe. If so, permanently disable script on the compartment by // calling Block() and throwing away the key. nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(aDocument->GetChannel()); if (jarChannel && jarChannel->GetIsUnsafe()) { - xpc::Scriptability::Get(newInnerWindow->mJSObject).Block(); + xpc::Scriptability::Get(newInnerGlobal).Block(); } if (mArguments) { newInnerWindow->DefineArgumentsProperty(mArguments); mArguments = nullptr; } // Give the new inner window our chrome event handler (since it @@ -2614,17 +2627,17 @@ nsGlobalWindow::SetNewDocument(nsIDocume } mContext->GC(JS::gcreason::SET_NEW_DOCUMENT); mContext->DidInitializeContext(); // We wait to fire the debugger hook until the window is all set up and hooked // up with the outer. See bug 969156. if (createdInnerWindow) { - JS::Rooted<JSObject*> global(cx, newInnerWindow->mJSObject); + JS::Rooted<JSObject*> global(cx, newInnerWindow->GetWrapper()); JS_FireOnNewGlobalObject(cx, global); } if (newInnerWindow && !newInnerWindow->mHasNotifiedGlobalCreated && mDoc) { // We should probably notify. However if this is the, arguably bad, // situation when we're creating a temporary non-chrome-about-blank // document in a chrome docshell, don't notify just yet. Instead wait // until we have a real chrome doc. @@ -2783,18 +2796,17 @@ nsGlobalWindow::SetDocShell(nsIDocShell* void nsGlobalWindow::DetachFromDocShell() { NS_ASSERTION(IsOuterWindow(), "Uh, DetachFromDocShell() called on inner window!"); // DetachFromDocShell means the window is being torn down. Drop our // reference to the script context, allowing it to be deleted // later. Meanwhile, keep our weak reference to the script object - // (mJSObject) so that it can be retrieved later (until it is - // finalized by the JS GC). + // so that it can be retrieved later (until it is finalized by the JS GC). NS_ASSERTION(mTimeouts.isEmpty(), "Uh, outer window holds timeouts!"); // Call FreeInnerObjects on all inner windows, not just the current // one, since some could be held by WindowStateHolder objects that // are GC-owned. for (nsRefPtr<nsGlobalWindow> inner = (nsGlobalWindow *)PR_LIST_HEAD(this); inner != this; @@ -3208,29 +3220,21 @@ nsGlobalWindow::DispatchDOMEvent(WidgetE nsEventStatus* aEventStatus) { return EventDispatcher::DispatchDOMEvent(static_cast<nsPIDOMWindow*>(this), aEvent, aDOMEvent, aPresContext, aEventStatus); } void -nsGlobalWindow::OnFinalize(JSObject* aObject) -{ - if (aObject == mJSObject) { - mJSObject = nullptr; - } -} - -void nsGlobalWindow::PoisonOuterWindowProxy(JSObject *aObject) { MOZ_ASSERT(IsOuterWindow()); - if (aObject == mJSObject) { - mJSObject.setToCrashOnTouch(); + if (aObject == GetWrapperPreserveColor()) { + PoisonWrapper(); } } nsresult nsGlobalWindow::SetArguments(nsIArray *aArguments) { MOZ_ASSERT(IsOuterWindow()); nsresult rv; @@ -3268,17 +3272,17 @@ nsresult nsGlobalWindow::DefineArgumentsProperty(nsIArray *aArguments) { MOZ_ASSERT(!mIsModalContentWindow); // Handled separately. nsIScriptContext *ctx = GetOuterWindowInternal()->mContext; NS_ENSURE_TRUE(aArguments && ctx, NS_ERROR_NOT_INITIALIZED); AutoPushJSContext cx(ctx->GetNativeContext()); NS_ENSURE_TRUE(cx, NS_ERROR_NOT_INITIALIZED); - JS::Rooted<JSObject*> obj(cx, mJSObject); + JS::Rooted<JSObject*> obj(cx, GetWrapperPreserveColor()); return GetContextInternal()->SetProperty(obj, "arguments", aArguments); } //***************************************************************************** // nsGlobalWindow::nsIScriptObjectPrincipal //***************************************************************************** nsIPrincipal* @@ -4384,20 +4388,20 @@ nsGlobalWindow::GetOpener(nsIDOMWindow** return rv.ErrorCode(); } void nsGlobalWindow::SetOpener(nsIDOMWindow* aOpener, ErrorResult& aError) { // Check if we were called from a privileged chrome script. If not, and if // aOpener is not null, just define aOpener on our inner window's JS object, - // wapped into the current compartment so that for Xrays we define on the Xray - // expando object, but don't set it on the outer window, so that it'll get - // reset on navigation. This is just like replaceable properties, but we're - // not quite readonly. + // wrapped into the current compartment so that for Xrays we define on the + // Xray expando object, but don't set it on the outer window, so that it'll + // get reset on navigation. This is just like replaceable properties, but + // we're not quite readonly. if (aOpener && !nsContentUtils::IsCallerChrome()) { // JS_WrapObject will outerize, so we don't care if aOpener is an inner. nsCOMPtr<nsIGlobalObject> glob = do_QueryInterface(aOpener); if (!glob) { aError.Throw(NS_ERROR_UNEXPECTED); return; } @@ -4408,18 +4412,18 @@ nsGlobalWindow::SetOpener(nsIDOMWindow* // expandos on Xrays as needed. JS::Rooted<JSObject*> otherObj(cx, glob->GetGlobalJSObject()); if (!otherObj) { aError.Throw(NS_ERROR_UNEXPECTED); return; } - JS::Rooted<JSObject*> thisObj(cx, mJSObject); - if (!mJSObject) { + JS::Rooted<JSObject*> thisObj(cx, GetWrapperPreserveColor()); + if (!thisObj) { aError.Throw(NS_ERROR_UNEXPECTED); return; } if (!JS_WrapObject(cx, &otherObj) || !JS_WrapObject(cx, &thisObj) || !JS_DefineProperty(cx, thisObj, "opener", JS::ObjectValue(*otherObj), JS_PropertyStub, JS_StrictPropertyStub, @@ -5093,18 +5097,18 @@ nsGlobalWindow::RequestAnimationFrame(co { FORWARD_TO_INNER_OR_THROW(RequestAnimationFrame, (aCallback, aError), aError, 0); if (!mDoc) { return 0; } - if (mJSObject) { - js::NotifyAnimationActivity(mJSObject); + if (GetWrapperPreserveColor()) { + js::NotifyAnimationActivity(GetWrapperPreserveColor()); } int32_t handle; aError = mDoc->ScheduleFrameRequestCallback(aCallback, &handle); return handle; } NS_IMETHODIMP @@ -5655,17 +5659,17 @@ nsGlobalWindow::DispatchResizeEvent(cons ErrorResult res; nsRefPtr<Event> domEvent = mDoc->CreateEvent(NS_LITERAL_STRING("CustomEvent"), res); if (res.Failed()) { return false; } AutoSafeJSContext cx; - JSAutoCompartment ac(cx, mJSObject); + JSAutoCompartment ac(cx, GetWrapperPreserveColor()); DOMWindowResizeEventDetail detail; detail.mWidth = aSize.width; detail.mHeight = aSize.height; JS::Rooted<JS::Value> detailValue(cx); if (!detail.ToObject(cx, &detailValue)) { return false; } @@ -5692,17 +5696,17 @@ nsGlobalWindow::DispatchResizeEvent(cons return defaultActionEnabled; } void nsGlobalWindow::RefreshCompartmentPrincipal() { FORWARD_TO_INNER(RefreshCompartmentPrincipal, (), /* void */ ); - JS_SetCompartmentPrincipals(js::GetObjectCompartment(mJSObject), + JS_SetCompartmentPrincipals(js::GetObjectCompartment(GetWrapperPreserveColor()), nsJSPrincipals::get(mDoc->NodePrincipal())); } static already_AddRefed<nsIDocShellTreeItem> GetCallerDocShellTreeItem() { JSContext *cx = nsContentUtils::GetCurrentJSContext(); nsCOMPtr<nsIDocShellTreeItem> callerItem; @@ -8650,20 +8654,17 @@ nsGlobalWindow::GetCachedXBLPrototypeHan } void nsGlobalWindow::CacheXBLPrototypeHandler(nsXBLPrototypeHandler* aKey, JS::Handle<JSObject*> aHandler) { if (!mCachedXBLPrototypeHandlers) { mCachedXBLPrototypeHandlers = new nsJSThingHashtable<nsPtrHashKey<nsXBLPrototypeHandler>, JSObject*>(); - } - - if (!mCachedXBLPrototypeHandlers->Count()) { - mozilla::HoldJSObjects(this); + PreserveWrapper(ToSupports(this)); } mCachedXBLPrototypeHandlers->Put(aKey, aHandler); } /** * GetScriptableFrameElement is called when script reads * nsIGlobalWindow::frameElement. @@ -10402,16 +10403,22 @@ nsGlobalWindow::GetInterface(const nsIID } else { return QueryInterface(aIID, aSink); } return *aSink ? NS_OK : NS_ERROR_NO_INTERFACE; } +JS::Value +nsGlobalWindow::GetInterface(JSContext* aCx, nsIJSID* aIID, ErrorResult& aError) +{ + return dom::GetInterface(aCx, this, aIID, aError); +} + void nsGlobalWindow::FireOfflineStatusEvent() { if (!IsCurrentInnerWindow()) return; nsAutoString name; if (NS_IsOffline()) { name.AssignLiteral("offline"); @@ -12535,17 +12542,17 @@ nsGlobalWindow::EnsureSizeUpToDate() } } already_AddRefed<nsISupports> nsGlobalWindow::SaveWindowState() { NS_PRECONDITION(IsOuterWindow(), "Can't save the inner window's state"); - if (!mContext || !mJSObject) { + if (!mContext || !GetWrapperPreserveColor()) { // The window may be getting torn down; don't bother saving state. return nullptr; } nsGlobalWindow *inner = GetCurrentInnerWindowInternal(); NS_ASSERTION(inner, "No inner window to save"); // Don't do anything else to this inner window! After this point, all @@ -12565,17 +12572,17 @@ nsGlobalWindow::SaveWindowState() return state.forget(); } nsresult nsGlobalWindow::RestoreWindowState(nsISupports *aState) { NS_ASSERTION(IsOuterWindow(), "Cannot restore an inner window"); - if (!mContext || !mJSObject) { + if (!mContext || !GetWrapperPreserveColor()) { // The window may be getting torn down; don't bother restoring state. return NS_OK; } nsCOMPtr<WindowStateHolder> holder = do_QueryInterface(aState); NS_ENSURE_TRUE(holder, NS_ERROR_FAILURE); #ifdef DEBUG_PAGE_CACHE @@ -13416,94 +13423,163 @@ nsGlobalWindow::GetMessageManager(ErrorR MM_CHROME | MM_BROADCASTER); } return myself->mMessageManager; } // nsGlobalModalWindow implementation // QueryInterface implementation for nsGlobalModalWindow -NS_IMPL_CYCLE_COLLECTION_INHERITED_1(nsGlobalModalWindow, - nsGlobalWindow, - mReturnValue) - DOMCI_DATA(ModalContentWindow, nsGlobalModalWindow) -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsGlobalModalWindow) +NS_INTERFACE_MAP_BEGIN(nsGlobalModalWindow) NS_INTERFACE_MAP_ENTRY(nsIDOMModalContentWindow) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(ModalContentWindow) NS_INTERFACE_MAP_END_INHERITING(nsGlobalWindow) NS_IMPL_ADDREF_INHERITED(nsGlobalModalWindow, nsGlobalWindow) NS_IMPL_RELEASE_INHERITED(nsGlobalModalWindow, nsGlobalWindow) +JS::Value +nsGlobalWindow::GetDialogArguments(JSContext* aCx, ErrorResult& aError) +{ + FORWARD_TO_OUTER_OR_THROW(GetDialogArguments, (aCx, aError), aError, + JS::UndefinedValue()); + + MOZ_ASSERT(IsModalContentWindow(), + "This should only be called on modal windows!"); + + // This does an internal origin check, and returns undefined if the subject + // does not subsumes the origin of the arguments. + JS::Rooted<JSObject*> wrapper(aCx, GetWrapper()); + JSAutoCompartment ac(aCx, wrapper); + JS::Rooted<JS::Value> args(aCx); + mDialogArguments->Get(aCx, wrapper, nsContentUtils::GetSubjectPrincipal(), + &args, aError); + return args; +} + NS_IMETHODIMP nsGlobalModalWindow::GetDialogArguments(nsIVariant **aArguments) { FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(GetDialogArguments, (aArguments), NS_ERROR_NOT_INITIALIZED); // This does an internal origin check, and returns undefined if the subject // does not subsumes the origin of the arguments. return mDialogArguments->Get(nsContentUtils::GetSubjectPrincipal(), aArguments); } +JS::Value +nsGlobalWindow::GetReturnValue(JSContext* aCx, ErrorResult& aError) +{ + FORWARD_TO_OUTER_OR_THROW(GetReturnValue, (aCx, aError), aError, + JS::UndefinedValue()); + + MOZ_ASSERT(IsModalContentWindow(), + "This should only be called on modal windows!"); + + JS::Rooted<JS::Value> returnValue(aCx); + if (mReturnValue) { + JS::Rooted<JSObject*> wrapper(aCx, GetWrapper()); + JSAutoCompartment ac(aCx, wrapper); + mReturnValue->Get(aCx, wrapper, nsContentUtils::GetSubjectPrincipal(), + &returnValue, aError); + } + return returnValue; +} + NS_IMETHODIMP nsGlobalModalWindow::GetReturnValue(nsIVariant **aRetVal) { FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(GetReturnValue, (aRetVal), NS_OK); nsCOMPtr<nsIVariant> result; if (!mReturnValue) { nsCOMPtr<nsIVariant> variant = CreateVoidVariant(); variant.forget(aRetVal); return NS_OK; } return mReturnValue->Get(nsContentUtils::GetSubjectPrincipal(), aRetVal); } +void +nsGlobalWindow::SetReturnValue(JSContext* aCx, + JS::Handle<JS::Value> aReturnValue, + ErrorResult& aError) +{ + FORWARD_TO_OUTER_OR_THROW(SetReturnValue, (aCx, aReturnValue, aError), + aError, ); + + MOZ_ASSERT(IsModalContentWindow(), + "This should only be called on modal windows!"); + + nsCOMPtr<nsIVariant> returnValue; + aError = + nsContentUtils::XPConnect()->JSToVariant(aCx, aReturnValue, + getter_AddRefs(returnValue)); + if (!aError.Failed()) { + mReturnValue = new DialogValueHolder(nsContentUtils::GetSubjectPrincipal(), + returnValue); + } +} + NS_IMETHODIMP nsGlobalModalWindow::SetReturnValue(nsIVariant *aRetVal) { FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(SetReturnValue, (aRetVal), NS_OK); mReturnValue = new DialogValueHolder(nsContentUtils::GetSubjectPrincipal(), aRetVal); return NS_OK; } +/* static */ +bool +nsGlobalWindow::IsModalContentWindow(JSContext* aCx, JSObject* aGlobal) +{ + // For now, have to deal with XPConnect objects here. + nsGlobalWindow* win; + nsresult rv = UNWRAP_OBJECT(Window, aGlobal, win); + if (NS_FAILED(rv)) { + nsCOMPtr<nsPIDOMWindow> piWin = do_QueryWrapper(aCx, aGlobal); + win = static_cast<nsGlobalWindow*>(piWin.get()); + } + return win->IsModalContentWindow(); +} + NS_IMETHODIMP nsGlobalWindow::GetConsole(JSContext* aCx, JS::MutableHandle<JS::Value> aConsole) { ErrorResult rv; nsRefPtr<Console> console = GetConsole(rv); if (rv.Failed()) { return rv.ErrorCode(); } - JS::Rooted<JSObject*> thisObj(aCx, mJSObject); - if (!mJSObject) { + JS::Rooted<JSObject*> thisObj(aCx, GetWrapper()); + if (!thisObj) { return NS_ERROR_UNEXPECTED; } if (!JS_WrapObject(aCx, &thisObj) || !WrapNewBindingObject(aCx, thisObj, console, aConsole)) { return NS_ERROR_FAILURE; } return NS_OK; } NS_IMETHODIMP nsGlobalWindow::SetConsole(JSContext* aCx, JS::Handle<JS::Value> aValue) { - JS::Rooted<JSObject*> thisObj(aCx, mJSObject); - if (!mJSObject) { + JS::Rooted<JSObject*> thisObj(aCx, GetWrapper()); + if (!thisObj) { return NS_ERROR_UNEXPECTED; } if (!JS_WrapObject(aCx, &thisObj) || !JS_DefineProperty(aCx, thisObj, "console", aValue, JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE)) { return NS_ERROR_FAILURE;
--- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -79,16 +79,17 @@ class nsIArray; class nsIBaseWindow; class nsIContent; class nsICSSDeclaration; class nsIDocShellTreeOwner; class nsIDOMCrypto; class nsIDOMOfflineResourceList; class nsIScrollableFrame; class nsIControllers; +class nsIJSID; class nsIScriptContext; class nsIScriptTimeoutHandler; class nsIWebBrowserChrome; class nsDOMWindowList; class nsLocation; class nsScreen; class nsHistory; @@ -272,16 +273,26 @@ public: if (aSubject->SubsumesConsideringDomain(mOrigin)) { result = mValue; } else { result = CreateVoidVariant(); } result.forget(aResult); return NS_OK; } + void Get(JSContext* aCx, JS::Handle<JSObject*> aScope, nsIPrincipal* aSubject, + JS::MutableHandle<JS::Value> aResult, mozilla::ErrorResult& aError) + { + if (aSubject->Subsumes(mOrigin)) { + aError = nsContentUtils::XPConnect()->VariantToJS(aCx, aScope, + mValue, aResult); + } else { + aResult.setUndefined(); + } + } virtual ~DialogValueHolder() {} private: nsCOMPtr<nsIPrincipal> mOrigin; nsCOMPtr<nsIVariant> mValue; }; //***************************************************************************** // nsGlobalWindow: Global Object for Scripting @@ -343,29 +354,31 @@ public: } // nsIGlobalJSObjectHolder virtual JSObject *GetGlobalJSObject(); // nsIScriptGlobalObject JSObject *FastGetGlobalJSObject() const { - return mJSObject; + return GetWrapperPreserveColor(); } + void TraceGlobalJSObject(JSTracer* aTrc); virtual nsresult EnsureScriptEnvironment(); virtual nsIScriptContext *GetScriptContext(); void PoisonOuterWindowProxy(JSObject *aObject); - virtual void OnFinalize(JSObject* aObject); virtual bool IsBlackForCC(bool aTracingNeeded = true); + static JSObject* OuterObject(JSContext* aCx, JS::HandleObject aObj); + // nsIScriptObjectPrincipal virtual nsIPrincipal* GetPrincipal(); // nsIDOMWindow NS_DECL_NSIDOMWINDOW #ifdef MOZ_B2G // nsIDOMWindowB2G @@ -576,16 +589,19 @@ public: return mCreatingInnerWindow; } bool IsChromeWindow() const { return mIsChrome; } + using nsPIDOMWindow::IsModalContentWindow; + static bool IsModalContentWindow(JSContext* aCx, JSObject* aGlobal); + // GetScrollFrame does not flush. Callers should do it themselves as needed, // depending on which info they actually want off the scrollable frame. nsIScrollableFrame *GetScrollFrame(); nsresult Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData); // Outer windows only. @@ -977,16 +993,24 @@ public: void Restore(mozilla::ErrorResult& aError); void NotifyDefaultButtonLoaded(mozilla::dom::Element& aDefaultButton, mozilla::ErrorResult& aError); nsIMessageBroadcaster* GetMessageManager(mozilla::ErrorResult& aError); void BeginWindowMove(mozilla::dom::Event& aMouseDownEvent, mozilla::dom::Element* aPanel, mozilla::ErrorResult& aError); + JS::Value GetDialogArguments(JSContext* aCx, mozilla::ErrorResult& aError); + JS::Value GetReturnValue(JSContext* aCx, mozilla::ErrorResult& aError); + void SetReturnValue(JSContext* aCx, JS::Handle<JS::Value> aReturnValue, + mozilla::ErrorResult& aError); + + JS::Value GetInterface(JSContext* aCx, nsIJSID* aIID, + mozilla::ErrorResult& aError); + protected: // Array of idle observers that are notified of idle events. nsTObserverArray<IdleObserverHolder> mIdleObservers; // Idle timer used for function callbacks to notify idle observers. nsCOMPtr<nsITimer> mIdleTimer; // Idle fuzz time added to idle timer callbacks. @@ -1435,16 +1459,19 @@ protected: nsCOMPtr<nsIControllers> mControllers; // For |window.arguments|, via |openDialog|. nsCOMPtr<nsIArray> mArguments; // For |window.dialogArguments|, via |showModalDialog|. nsRefPtr<DialogValueHolder> mDialogArguments; + // Only used in the outer. + nsRefPtr<DialogValueHolder> mReturnValue; + nsRefPtr<mozilla::dom::Navigator> mNavigator; nsRefPtr<nsScreen> mScreen; nsRefPtr<nsDOMWindowList> mFrames; nsRefPtr<mozilla::dom::BarProp> mMenubar; nsRefPtr<mozilla::dom::BarProp> mToolbar; nsRefPtr<mozilla::dom::BarProp> mLocationbar; nsRefPtr<mozilla::dom::BarProp> mPersonalbar; nsRefPtr<mozilla::dom::BarProp> mStatusbar; @@ -1480,19 +1507,16 @@ protected: uint32_t mTimeoutPublicIdCounter; uint32_t mTimeoutFiringDepth; nsRefPtr<nsLocation> mLocation; nsRefPtr<nsHistory> mHistory; // These member variables are used on both inner and the outer windows. nsCOMPtr<nsIPrincipal> mDocumentPrincipal; - // The JS global object. Global objects are always allocated tenured. - JS::TenuredHeap<JSObject*> mJSObject; - typedef nsCOMArray<nsIDOMStorageEvent> nsDOMStorageEventArray; nsDOMStorageEventArray mPendingStorageEvents; uint32_t mTimeoutsSuspendDepth; // the method that was used to focus mFocusedNode uint32_t mFocusMethod; @@ -1634,22 +1658,16 @@ public: nsGlobalModalWindow(nsGlobalWindow *aOuterWindow) : nsGlobalWindow(aOuterWindow) { mIsModalContentWindow = true; } NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIDOMMODALCONTENTWINDOW - - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsGlobalModalWindow, nsGlobalWindow) - -protected: - // For use by outer windows only. - nsRefPtr<DialogValueHolder> mReturnValue; }; /* factory function */ inline already_AddRefed<nsGlobalWindow> NS_NewScriptGlobalObject(bool aIsChrome, bool aIsModalContentWindow) { nsRefPtr<nsGlobalWindow> global;
--- a/dom/base/nsIScriptGlobalObject.h +++ b/dom/base/nsIScriptGlobalObject.h @@ -28,18 +28,18 @@ struct ErrorEventInit; // aStatus will be filled in with the status. bool NS_HandleScriptError(nsIScriptGlobalObject *aScriptGlobal, const mozilla::dom::ErrorEventInit &aErrorEvent, nsEventStatus *aStatus); #define NS_ISCRIPTGLOBALOBJECT_IID \ -{ 0x6995e1ff, 0x9fc5, 0x44a7, \ - { 0xbd, 0x7c, 0xe7, 0xcd, 0x44, 0x47, 0x22, 0x87 } } +{ 0x876f83bd, 0x6314, 0x460a, \ + { 0xa0, 0x45, 0x1c, 0x8f, 0x46, 0x2f, 0xb8, 0xe1 } } /** * The global object which keeps a script context for each supported script * language. This often used to store per-window global state. * This is a heavyweight interface implemented only by DOM globals, and * it might go away some time in the future. */ @@ -63,25 +63,16 @@ public: */ virtual nsIScriptContext *GetScriptContext() = 0; nsIScriptContext* GetContext() { return GetScriptContext(); } /** - * Called when the global script for a language is finalized, typically as - * part of its GC process. By the time this call is made, the - * nsIScriptContext for the language has probably already been removed. - * After this call, the passed object is dead - which should generally be the - * same object the global is using for a global for that language. - */ - virtual void OnFinalize(JSObject* aObject) = 0; - - /** * Handle a script error. Generally called by a script context. */ virtual nsresult HandleScriptError( const mozilla::dom::ErrorEventInit &aErrorEventInit, nsEventStatus *aEventStatus) { NS_ENSURE_STATE(NS_HandleScriptError(this, aErrorEventInit, aEventStatus)); return NS_OK; }
--- a/dom/base/nsWrapperCache.h +++ b/dom/base/nsWrapperCache.h @@ -6,18 +6,18 @@ #ifndef nsWrapperCache_h___ #define nsWrapperCache_h___ #include "nsCycleCollectionParticipant.h" #include "mozilla/Assertions.h" #include "js/Id.h" // must come before js/RootingAPI.h #include "js/Value.h" // must come before js/RootingAPI.h #include "js/RootingAPI.h" +#include "js/Tracer.h" -struct JSTracer; class XPCWrappedNativeScope; #define NS_WRAPPERCACHE_IID \ { 0x6f3179a1, 0x36f7, 0x4a5c, \ { 0x8c, 0xf1, 0xad, 0xc8, 0x7c, 0xde, 0x3e, 0x87 } } /** * Class to store the wrapper for an object. This can only be used with objects @@ -217,16 +217,31 @@ public: #ifdef DEBUG // Make sure the cycle collector will be able to traverse to the wrapper. CheckCCWrapperTraversal(aScriptObjectHolder, aTracer); #endif } void ReleaseWrapper(void* aScriptObjectHolder); +protected: + void TraceWrapper(JSTracer* aTrc, const char* name) + { + if (mWrapper) { + JS_CallHeapObjectTracer(aTrc, &mWrapper, name); + } + } + + void PoisonWrapper() + { + if (mWrapper) { + mWrapper.setToCrashOnTouch(); + } + } + private: JSObject *GetWrapperJSObject() const { return mWrapper; } void SetWrapperJSObject(JSObject* aWrapper) {
--- a/dom/bindings/BindingDeclarations.h +++ b/dom/bindings/BindingDeclarations.h @@ -20,20 +20,16 @@ #include "nsCOMPtr.h" #include "nsTArray.h" #include "nsAutoPtr.h" // for nsRefPtr member variables #include "mozilla/dom/DOMString.h" #include "mozilla/dom/OwningNonNull.h" class nsWrapperCache; -// nsGlobalWindow implements nsWrapperCache, but doesn't always use it. Don't -// try to use it without fixing that first. -class nsGlobalWindow; - namespace mozilla { namespace dom { // Struct that serves as a base class for all dictionaries. Particularly useful // so we can use IsBaseOf to detect dictionary template arguments. struct DictionaryBase { protected: @@ -438,22 +434,16 @@ public: inline nsWrapperCache* GetWrapperCache(nsWrapperCache* cache) { return cache; } inline nsWrapperCache* -GetWrapperCache(nsGlobalWindow*) -{ - return nullptr; -} - -inline nsWrapperCache* GetWrapperCache(void* p) { return nullptr; } // Helper template for smart pointers to resolve ambiguity between // GetWrappeCache(void*) and GetWrapperCache(const ParentObject&). template <template <typename> class SmartPtr, typename T>
--- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -810,23 +810,30 @@ QueryInterface(JSContext* cx, unsigned a JS::CallArgs args = JS::CallArgsFromVp(argc, vp); JS::Rooted<JS::Value> thisv(cx, JS_THIS(cx, vp)); if (thisv.isNull()) return false; // Get the object. It might be a security wrapper, in which case we do a checked // unwrap. JS::Rooted<JSObject*> origObj(cx, &thisv.toObject()); - JSObject* obj = js::CheckedUnwrap(origObj); + JSObject* obj = js::CheckedUnwrap(origObj, /* stopAtOuter = */ false); if (!obj) { JS_ReportError(cx, "Permission denied to access object"); return false; } - nsISupports* native = UnwrapDOMObjectToISupports(obj); + // Switch this to UnwrapDOMObjectToISupports once our global objects are + // using new bindings. + JS::Rooted<JS::Value> val(cx, JS::ObjectValue(*obj)); + nsISupports* native = nullptr; + nsCOMPtr<nsISupports> nativeRef; + xpc_qsUnwrapArg<nsISupports>(cx, val, &native, + static_cast<nsISupports**>(getter_AddRefs(nativeRef)), + &val); if (!native) { return Throw(cx, NS_ERROR_FAILURE); } if (argc < 1) { return Throw(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS); } @@ -857,16 +864,39 @@ QueryInterface(JSContext* cx, unsigned a if (NS_FAILED(rv)) { return Throw(cx, rv); } *vp = thisv; return true; } +JS::Value +GetInterfaceImpl(JSContext* aCx, nsIInterfaceRequestor* aRequestor, + nsWrapperCache* aCache, nsIJSID* aIID, ErrorResult& aError) +{ + const nsID* iid = aIID->GetID(); + + nsRefPtr<nsISupports> result; + aError = aRequestor->GetInterface(*iid, getter_AddRefs(result)); + if (aError.Failed()) { + return JS::NullValue(); + } + + JS::Rooted<JSObject*> wrapper(aCx, aCache->GetWrapper()); + JS::Rooted<JSObject*> global(aCx, js::GetGlobalForObjectCrossCompartment(wrapper)); + JS::Rooted<JS::Value> v(aCx, JSVAL_NULL); + if (!WrapObject(aCx, global, result, iid, &v)) { + aError.Throw(NS_ERROR_FAILURE); + return JS::NullValue(); + } + + return v; +} + bool ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp) { return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR); } bool ThrowConstructorWithoutNew(JSContext* cx, const char* name)
--- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -29,16 +29,17 @@ #include "MainThreadUtils.h" #include "nsISupportsImpl.h" #include "qsObjectHelper.h" #include "xpcpublic.h" #include "nsIVariant.h" #include "nsWrapperCacheInlines.h" +class nsIJSID; class nsPIDOMWindow; extern nsresult xpc_qsUnwrapArgImpl(JSContext* cx, JS::Handle<JS::Value> v, const nsIID& iid, void** ppArg, nsISupports** ppArgRef, JS::MutableHandle<JS::Value> vp); namespace mozilla { namespace dom { @@ -1601,16 +1602,27 @@ WantsQueryInterface static_assert(IsBaseOf<nsISupports, T>::value, "QueryInterface can't work without an nsISupports."); static bool Enabled(JSContext* aCx, JSObject* aGlobal) { return NS_IsMainThread() && IsChromeOrXBL(aCx, aGlobal); } }; +JS::Value +GetInterfaceImpl(JSContext* aCx, nsIInterfaceRequestor* aRequestor, + nsWrapperCache* aCache, nsIJSID* aIID, ErrorResult& aError); + +template<class T> +JS::Value +GetInterface(JSContext* aCx, T* aThis, nsIJSID* aIID, ErrorResult& aError) +{ + return GetInterfaceImpl(aCx, aThis, aThis, aIID, aError); +} + bool ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp); bool ThrowConstructorWithoutNew(JSContext* cx, const char* name); // vp is allowed to be null; in that case no get will be attempted, // and *found will simply indicate whether the property exists.
--- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -48,17 +48,18 @@ def isTypeCopyConstructible(type): CGUnionStruct.isUnionCopyConstructible(type)) or (type.isDictionary() and CGDictionary.isDictionaryCopyConstructible(type.inner))) def wantsAddProperty(desc): return (desc.concrete and desc.wrapperCache and - not desc.interface.getExtendedAttribute("Global")) + not (desc.workers and + desc.interface.getExtendedAttribute("Global"))) # We'll want to insert the indent at the beginnings of lines, but we # don't want to indent empty lines. So only indent lines that have a # non-newline character on them. lineStartDetector = re.compile("^(?=[^\n#])", re.MULTILINE) @@ -325,19 +326,56 @@ class CGDOMJSClass(CGThing): def declare(self): return "" def define(self): traceHook = 'nullptr' callHook = LEGACYCALLER_HOOK_NAME if self.descriptor.operations["LegacyCaller"] else 'nullptr' slotCount = INSTANCE_RESERVED_SLOTS + self.descriptor.interface.totalMembersInSlots classFlags = "JSCLASS_IS_DOMJSCLASS | " + classExtensionAndObjectOps = """\ +JS_NULL_CLASS_EXT, +JS_NULL_OBJECT_OPS +""" if self.descriptor.interface.getExtendedAttribute("Global"): classFlags += "JSCLASS_DOM_GLOBAL | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS) | JSCLASS_IMPLEMENTS_BARRIERS" traceHook = "JS_GlobalObjectTraceHook" + if not self.descriptor.workers: + classExtensionAndObjectOps = """\ +{ + nsGlobalWindow::OuterObject, /* outerObject */ + nullptr, /* innerObject */ + nullptr, /* iteratorObject */ + false, /* isWrappedNative */ + nullptr /* weakmapKeyDelegateOp */ +}, +{ + nullptr, /* lookupGeneric */ + nullptr, /* lookupProperty */ + nullptr, /* lookupElement */ + nullptr, /* defineGeneric */ + nullptr, /* defineProperty */ + nullptr, /* defineElement */ + nullptr, /* getGeneric */ + nullptr, /* getProperty */ + nullptr, /* getElement */ + nullptr, /* setGeneric */ + nullptr, /* setProperty */ + nullptr, /* setElement */ + nullptr, /* getGenericAttributes */ + nullptr, /* setGenericAttributes */ + nullptr, /* deleteProperty */ + nullptr, /* deleteElement */ + nullptr, /* watch */ + nullptr, /* unwatch */ + nullptr, /* slice */ + nullptr, /* enumerate */ + JS_ObjectToOuterObject /* thisObject */ +} +""" else: classFlags += "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount if self.descriptor.interface.getExtendedAttribute("NeedNewResolve"): newResolveHook = "(JSResolveOp)" + NEWRESOLVE_HOOK_NAME classFlags += " | JSCLASS_NEW_RESOLVE" enumerateHook = ENUMERATE_HOOK_NAME elif self.descriptor.interface.getExtendedAttribute("Global"): newResolveHook = "(JSResolveOp) mozilla::dom::ResolveGlobal" @@ -361,30 +399,30 @@ class CGDOMJSClass(CGThing): ${resolve}, /* resolve */ JS_ConvertStub, ${finalize}, /* finalize */ ${call}, /* call */ nullptr, /* hasInstance */ nullptr, /* construct */ ${trace}, /* trace */ JS_NULL_CLASS_SPEC, - JS_NULL_CLASS_EXT, - JS_NULL_OBJECT_OPS + $*{classExtensionAndObjectOps} }, $*{descriptor} }; """, name=self.descriptor.interface.identifier.name, flags=classFlags, addProperty=ADDPROPERTY_HOOK_NAME if wantsAddProperty(self.descriptor) else 'JS_PropertyStub', enumerate=enumerateHook, resolve=newResolveHook, finalize=FINALIZE_HOOK_NAME, call=callHook, trace=traceHook, + classExtensionAndObjectOps=classExtensionAndObjectOps, descriptor=DOMClass(self.descriptor)) class CGDOMProxyJSClass(CGThing): """ Generate a DOMJSClass for a given proxy descriptor """ def __init__(self, descriptor):
--- a/dom/contacts/ContactManager.js +++ b/dom/contacts/ContactManager.js @@ -24,16 +24,17 @@ XPCOMUtils.defineLazyServiceGetter(this, "nsIMessageSender"); const CONTACTS_SENDMORE_MINIMUM = 5; // We need this to create a copy of the mozContact object in ContactManager.save // Keep in sync with the interfaces. const PROPERTIES = [ "name", "honorificPrefix", "givenName", "additionalName", "familyName", + "phoneticGivenName", "phoneticFamilyName", "honorificSuffix", "nickname", "photo", "category", "org", "jobTitle", "bday", "note", "anniversary", "sex", "genderIdentity", "key", "adr", "email", "url", "impp", "tel" ]; let mozContactInitWarned = false; function Contact() { }
--- a/dom/contacts/fallback/ContactDB.jsm +++ b/dom/contacts/fallback/ContactDB.jsm @@ -17,17 +17,17 @@ const Ci = Components.interfaces; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/IndexedDBHelper.jsm"); Cu.import("resource://gre/modules/PhoneNumberUtils.jsm"); Cu.importGlobalProperties(["indexedDB"]); /* all exported symbols need to be bound to this on B2G - Bug 961777 */ this.DB_NAME = "contacts"; -this.DB_VERSION = 19; +this.DB_VERSION = 20; this.STORE_NAME = "contacts"; this.SAVED_GETALL_STORE_NAME = "getallcache"; const CHUNK_SIZE = 20; this.REVISION_STORE = "revision"; const REVISION_KEY = "revision"; function exportContact(aRecord) { if (aRecord) { @@ -164,16 +164,20 @@ ContactDB.prototype = { objectStore.createIndex("givenNameLowerCase", "search.givenName", { multiEntry: true }); objectStore.createIndex("nameLowerCase", "search.name", { multiEntry: true }); objectStore.createIndex("telLowerCase", "search.tel", { multiEntry: true }); objectStore.createIndex("emailLowerCase", "search.email", { multiEntry: true }); objectStore.createIndex("tel", "search.exactTel", { multiEntry: true }); objectStore.createIndex("category", "properties.category", { multiEntry: true }); objectStore.createIndex("email", "search.email", { multiEntry: true }); objectStore.createIndex("telMatch", "search.parsedTel", {multiEntry: true}); + objectStore.createIndex("phoneticFamilyName", "properties.phoneticFamilyName", { multiEntry: true }); + objectStore.createIndex("phoneticGivenName", "properties.phoneticGivenName", { multiEntry: true }); + objectStore.createIndex("phoneticFamilyNameLowerCase", "search.phoneticFamilyName", { multiEntry: true }); + objectStore.createIndex("phoneticGivenNameLowerCase", "search.phoneticGivenName", { multiEntry: true }); aDb.createObjectStore(SAVED_GETALL_STORE_NAME); aDb.createObjectStore(REVISION_STORE).put(0, REVISION_KEY); } let valueUpgradeSteps = []; function scheduleValueUpgrade(upgradeFunc) { var length = valueUpgradeSteps.push(upgradeFunc); @@ -700,16 +704,27 @@ ContactDB.prototype = { } }); } return true; }); next(); }, + function upgrade19to20() { + if (DEBUG) debug("upgrade19to20 create schema(phonetic)"); + if (!objectStore) { + objectStore = aTransaction.objectStore(STORE_NAME); + } + objectStore.createIndex("phoneticFamilyName", "properties.phoneticFamilyName", { multiEntry: true }); + objectStore.createIndex("phoneticGivenName", "properties.phoneticGivenName", { multiEntry: true }); + objectStore.createIndex("phoneticFamilyNameLowerCase", "search.phoneticFamilyName", { multiEntry: true }); + objectStore.createIndex("phoneticGivenNameLowerCase", "search.phoneticGivenName", { multiEntry: true }); + next(); + }, ]; let index = aOldVersion; let outer = this; /* This function runs all upgrade functions that are in the * valueUpgradeSteps array. These functions have the following properties: * - they must be synchronous @@ -819,16 +834,18 @@ ContactDB.prototype = { name: [], givenName: [], familyName: [], email: [], category: [], tel: [], exactTel: [], parsedTel: [], + phoneticFamilyName: [], + phoneticGivenName: [], }; for (let field in aContact.properties) { contact.properties[field] = aContact.properties[field]; // Add search fields if (aContact.properties[field] && contact.search[field]) { for (let i = 0; i <= aContact.properties[field].length; i++) { if (aContact.properties[field][i]) { @@ -1110,27 +1127,42 @@ ContactDB.prototype = { if (DEBUG) debug("getCount"); this.newTxn("readonly", STORE_NAME, function (txn, store) { store.count().onsuccess = function (e) { aSuccessCb(e.target.result); }; }, null, aErrorCb); }, + getSortByParam: function CDB_getSortByParam(aFindOptions) { + switch (aFindOptions.sortBy) { + case "familyName": + return [ "familyName", "givenName" ]; + case "givenName": + return [ "givenName" , "familyName" ]; + case "phoneticFamilyName": + return [ "phoneticFamilyName" , "phoneticGivenName" ]; + case "phoneticGivenName": + return [ "phoneticGivenName" , "phoneticFamilyName" ]; + default: + return [ "givenName" , "familyName" ]; + } + }, + /* * Sorting the contacts by sortBy field. aSortBy can either be familyName or givenName. * If 2 entries have the same sortyBy field or no sortBy field is present, we continue * sorting with the other sortyBy field. */ sortResults: function CDB_sortResults(aResults, aFindOptions) { if (!aFindOptions) return; if (aFindOptions.sortBy != "undefined") { const sortOrder = aFindOptions.sortOrder; - const sortBy = aFindOptions.sortBy == "familyName" ? [ "familyName", "givenName" ] : [ "givenName" , "familyName" ]; + const sortBy = this.getSortByParam(aFindOptions); aResults.sort(function (a, b) { let x, y; let result = 0; let xIndex = 0; let yIndex = 0; do {
--- a/dom/contacts/tests/shared.js +++ b/dom/contacts/tests/shared.js @@ -85,26 +85,30 @@ var adr2 = { countryName: "country2" }; var properties1 = { // please keep capital letters at the start of these names name: ["Test1 TestFamilyName", "Test2 Wagner"], familyName: ["TestFamilyName","Wagner"], givenName: ["Test1","Test2"], + phoneticFamilyName: ["TestphoneticFamilyName1","TestphoneticFamilyName2"], + phoneticGivenName: ["TestphoneticGivenName1","TestphoneticGivenName2"], nickname: ["nicktest"], tel: [{type: ["work"], value: "123456", carrier: "testCarrier"} , {type: ["home", "fax"], value: "+55 (31) 9876-3456"}, {type: ["home"], value: "+49 451 491934"}], adr: [adr1], email: [{type: ["work"], value: "x@y.com"}], }; var properties2 = { name: ["dummyHonorificPrefix dummyGivenName dummyFamilyName dummyHonorificSuffix", "dummyHonorificPrefix2"], familyName: ["dummyFamilyName"], givenName: ["dummyGivenName"], + phoneticFamilyName: ["dummyphoneticFamilyName"], + phoneticGivenName: ["dummyphoneticGivenName"], honorificPrefix: ["dummyHonorificPrefix","dummyHonorificPrefix2"], honorificSuffix: ["dummyHonorificSuffix"], additionalName: ["dummyadditionalName"], nickname: ["dummyNickname"], tel: [{type: ["test"], value: "7932012345", carrier: "myCarrier", pref: 1},{type: ["home", "custom"], value: "7932012346", pref: 0}], email: [{type: ["test"], value: "a@b.c"}, {value: "b@c.d", pref: 1}], adr: [adr1, adr2], impp: [{type: ["aim"], value:"im1", pref: 1}, {value: "im2"}], @@ -115,16 +119,96 @@ var properties2 = { url: [{type: ["work", "work2"], value: "www.1.com", pref: 1}, {value:"www2.com"}], bday: new Date("1980, 12, 01"), anniversary: new Date("2000, 12, 01"), sex: "male", genderIdentity: "test", key: ["ERPJ394GJJWEVJ0349GJ09W3H4FG0WFW80VHW3408GH30WGH348G3H"] }; +// To test sorting(CJK) +var c9 = { + phoneticFamilyName: ["a"], + phoneticGivenName: ["a"], +}; + +var c10 = { + phoneticFamilyName: ["b"], + phoneticGivenName: ["b"], +}; + +var c11 = { + phoneticFamilyName: ["c","a","b"], + phoneticGivenName: ["c","a","b"], +}; + +var c12 = { + phoneticFamilyName: ["c","a","c"], + phoneticGivenName: ["c","a","c"], +}; + +var c13 = { + phoneticFamilyName: [], + phoneticGivenName: [], +}; + +var c14 = { + phoneticFamilyName: ["e","e","e"], + phoneticGivenName: ["e","e","e"], +}; + +var c15 = { + phoneticFamilyName: ["e","e","e"], + phoneticGivenName: ["e","e","e"], +}; + +var c16 = { + phoneticFamilyName: ["e","e","e"], + phoneticGivenName: ["e","e","e"], +}; + +var properties3 = { + // please keep capital letters at the start of these names + name: ["Taro Yamada", "Ichiro Suzuki"], + familyName: ["Yamada","Suzuki"], + givenName: ["Taro","Ichiro"], + phoneticFamilyName: ["TestPhoneticFamilyYamada","TestPhoneticFamilySuzuki"], + phoneticGivenName: ["TestPhoneticGivenTaro","TestPhoneticGivenIchiro"], + nickname: ["phoneticNicktest"], + tel: [{type: ["work"], value: "123456", carrier: "testCarrier"} , {type: ["home", "fax"], value: "+55 (31) 9876-3456"}, {type: ["home"], value: "+49 451 491934"}], + adr: [adr1], + email: [{type: ["work"], value: "x@y.com"}], +}; + +var properties4 = { + name: ["dummyHonorificPrefix dummyTaro dummyYamada dummyHonorificSuffix", "dummyHonorificPrefix2"], + familyName: ["dummyYamada"], + givenName: ["dummyTaro"], + phoneticFamilyName: ["dummyTestPhoneticFamilyYamada"], + phoneticGivenName: ["dummyTestPhoneticGivenTaro"], + honorificPrefix: ["dummyPhoneticHonorificPrefix","dummyPhoneticHonorificPrefix2"], + honorificSuffix: ["dummyPhoneticHonorificSuffix"], + additionalName: ["dummyPhoneticAdditionalName"], + nickname: ["dummyPhoneticNickname"], + tel: [{type: ["test"], value: "7932012345", carrier: "myCarrier", pref: 1},{type: ["home", "custom"], value: "7932012346", pref: 0}], + email: [{type: ["test"], value: "a@b.c"}, {value: "b@c.d", pref: 1}], + adr: [adr1, adr2], + impp: [{type: ["aim"], value:"im1", pref: 1}, {value: "im2"}], + org: ["org1", "org2"], + jobTitle: ["boss", "superboss"], + note: ["test note"], + category: ["cat1", "cat2"], + url: [{type: ["work", "work2"], value: "www.1.com", pref: 1}, {value:"www2.com"}], + bday: new Date("1980, 12, 01"), + anniversary: new Date("2000, 12, 01"), + sex: "male", + genderIdentity: "test", + key: ["ERPJ394GJJWEVJ0349GJ09W3H4FG0WFW80VHW3408GH30WGH348G3H"] +}; + var sample_id1; var sample_id2; var createResult1; var createResult2; var findResult1; var findResult2; @@ -260,16 +344,18 @@ function checkContacts(contact1, contact ok(false, "Expected both contacts to be either present or absent"); return; } checkStrArray(contact1.name, contact2.name, "Same name"); checkStrArray(contact1.honorificPrefix, contact2.honorificPrefix, "Same honorificPrefix"); checkStrArray(contact1.givenName, contact2.givenName, "Same givenName"); checkStrArray(contact1.additionalName, contact2.additionalName, "Same additionalName"); checkStrArray(contact1.familyName, contact2.familyName, "Same familyName"); + checkStrArray(contact1.phoneticFamilyName, contact2.phoneticFamilyName, "Same phoneticFamilyName"); + checkStrArray(contact1.phoneticGivenName, contact2.phoneticGivenName, "Same phoneticGivenName"); checkStrArray(contact1.honorificSuffix, contact2.honorificSuffix, "Same honorificSuffix"); checkStrArray(contact1.nickname, contact2.nickname, "Same nickname"); checkCategory(contact1.category, contact2.category); checkStrArray(contact1.org, contact2.org, "Same org"); checkStrArray(contact1.jobTitle, contact2.jobTitle, "Same jobTitle"); is(contact1.bday ? contact1.bday.valueOf() : null, contact2.bday ? contact2.bday.valueOf() : null, "Same birthday"); checkStrArray(contact1.note, contact2.note, "Same note"); is(contact1.anniversary ? contact1.anniversary.valueOf() : null , contact2.anniversary ? contact2.anniversary.valueOf() : null, "Same anniversary");
--- a/dom/contacts/tests/test_contacts_basics2.html +++ b/dom/contacts/tests/test_contacts_basics2.html @@ -740,16 +740,18 @@ var steps = [ next(); }, function() { ok(true, "Inline changes to array properties should be seen by save"); var c = new mozContact({ name: [], familyName: [], givenName: [], + phoneticFamilyName: [], + phoneticGivenName: [], nickname: [], tel: [], adr: [], email: [] }); for (var prop of Object.getOwnPropertyNames(properties1)) { if (!Array.isArray(properties1[prop])) { continue; @@ -821,16 +823,311 @@ var steps = [ }; req.onerror = onFailure; }; req.onerror = onFailure; }; req.onerror = onFailure; }, function () { + ok(true, "Adding a new contact"); + createResult1 = new mozContact(properties3); + req = mozContacts.save(createResult1) + req.onsuccess = function () { + ok(createResult1.id, "The contact now has an ID."); + sample_id1 = createResult1.id; + next(); + } + req.onerror = onFailure; + }, + function () { + ok(true, "Adding a new contact2"); + createResult2 = new mozContact(properties4); + req = mozContacts.save(createResult2); + req.onsuccess = function () { + ok(createResult2.id, "The contact now has an ID."); + sample_id2 = createResult2.id; + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Retrieving all contacts"); + req = mozContacts.find({sortBy: "phoneticFamilyName"}); + req.onsuccess = function () { + is(req.result.length, 2, "Found exactly 2 contact."); + checkContacts(req.result[1], properties3); + next(); + } + req.onerror = onFailure; + }, + function () { + ok(true, "Searching contacts by query1"); + var options = {filterBy: ["phoneticGivenName", "email"], + filterOp: "startsWith", + filterValue: properties3.phoneticGivenName[0].substring(0, 3)} + req = mozContacts.find(options) + req.onsuccess = function () { + is(req.result.length, 1, "Found exactly 1 contact."); + findResult1 = req.result[0]; + ok(findResult1.id == sample_id1, "Same ID"); + checkContacts(findResult1, createResult1); + next(); + } + req.onerror = onFailure; + }, + function () { + ok(true, "Searching contacts by query2"); + var options = {filterBy: ["phoneticGivenName", "email"], + filterOp: "startsWith", + filterValue: properties4.phoneticGivenName[0].substring(0, 3)}; + req = mozContacts.find(options); + req.onsuccess = function () { + is(req.result.length, 1, "Found exactly 1 contact."); + findResult1 = req.result[0]; + is(findResult1.adr.length, 2, "Adr length 2"); + checkContacts(findResult1, createResult2); + next(); + } + req.onerror = onFailure; + }, + clearDatabase, + function () { + ok(true, "Adding 20 contacts"); + for (var i=0; i<19; i++) { + createResult1 = new mozContact(properties3); + req = mozContacts.save(createResult1); + req.onsuccess = function () { + ok(createResult1.id, "The contact now has an ID."); + }; + req.onerror = onFailure; + }; + createResult1 = new mozContact(properties3); + req = mozContacts.save(createResult1); + req.onsuccess = function () { + ok(createResult1.id, "The contact now has an ID."); + checkStrArray(createResult1.name, properties3.name, "Same Name"); + checkCount(20, "20 contacts in DB", next); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Retrieving all contacts"); + req = mozContacts.find(defaultOptions); + req.onsuccess = function () { + is(req.result.length, 20, "20 Entries."); + next(); + } + req.onerror = onFailure; + }, + function () { + ok(true, "Retrieving all contacts2"); + var options = {filterBy: ["phoneticGivenName"], + filterOp: "startsWith", + filterValue: properties3.phoneticGivenName[0].substring(0, 3)}; + req = mozContacts.find(options); + req.onsuccess = function () { + is(req.result.length, 20, "20 Entries."); + checkContacts(createResult1, req.result[19]); + next(); + } + req.onerror = onFailure; + }, + function () { + ok(true, "Retrieving all contacts3"); + var options = {filterBy: ["phoneticGivenName", "tel", "email"], + filterOp: "startsWith", + filterValue: properties3.phoneticGivenName[0].substring(0, 3)}; + req = mozContacts.find(options); + req.onsuccess = function () { + is(req.result.length, 20, "20 Entries."); + checkContacts(createResult1, req.result[10]); + next(); + } + req.onerror = onFailure; + }, + clearDatabase, + function () { + ok(true, "Testing clone contact"); + createResult1 = new mozContact(properties3); + req = mozContacts.save(createResult1); + req.onsuccess = function () { + ok(createResult1.id, "The contact now has an ID."); + checkStrArray(createResult1.phoneticFamilyName, properties3.phoneticFamilyName, "Same phoneticFamilyName"); + checkStrArray(createResult1.phoneticGivenName, properties3.phoneticGivenName, "Same phoneticGivenName"); + next(); + } + req.onerror = onFailure; + }, + function () { + ok(true, "Retrieving all contacts"); + req = mozContacts.find({sortBy: "phoneticGivenName"}); + req.onsuccess = function () { + is(req.result.length, 1, "1 Entries."); + next(); + } + req.onerror = onFailure; + }, + clearDatabase, + function () { + ok(true, "Test sorting"); + createResult1 = new mozContact(c11); + req = navigator.mozContacts.save(createResult1); + req.onsuccess = function () { + ok(createResult1.id, "The contact now has an ID."); + checkContacts(c11, createResult1); + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Test sorting"); + createResult1 = new mozContact(c10); + req = navigator.mozContacts.save(createResult1); + req.onsuccess = function () { + ok(createResult1.id, "The contact now has an ID."); + checkContacts(c10, createResult1); + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Test sorting"); + createResult1 = new mozContact(c12); + req = navigator.mozContacts.save(createResult1); + req.onsuccess = function () { + ok(createResult1.id, "The contact now has an ID."); + checkContacts(c12, createResult1); + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Test sorting"); + createResult1 = new mozContact(c9); + req = navigator.mozContacts.save(createResult1); + req.onsuccess = function () { + ok(createResult1.id, "The contact now has an ID."); + checkContacts(c9, createResult1); + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Test sorting"); + var options = {sortBy: "phoneticFamilyName", + sortOrder: "ascending"}; + req = navigator.mozContacts.find(options); + req.onsuccess = function () { + is(req.result.length, 4, "4 results"); + checkContacts(req.result[0], c9); + checkContacts(req.result[1], c10); + checkContacts(req.result[2], c11); + checkContacts(req.result[3], c12); + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Test sorting"); + var options = {sortBy: "phoneticFamilyName", + sortOrder: "descending"}; + req = navigator.mozContacts.find(options); + req.onsuccess = function () { + is(req.result.length, 4, "4 results"); + checkContacts(req.result[0], c12); + checkContacts(req.result[1], c11); + checkContacts(req.result[2], c10); + checkContacts(req.result[3], c9); + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Test sorting"); + createResult1 = new mozContact(c13); + req = navigator.mozContacts.save(createResult1); + req.onsuccess = function () { + ok(createResult1.id, "The contact now has an ID."); + checkContacts(c13, createResult1); + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Test sorting with empty string"); + var options = {sortBy: "phoneticFamilyName", + sortOrder: "ascending"}; + req = navigator.mozContacts.find(options); + req.onsuccess = function () { + is(req.result.length, 5, "5 results"); + checkContacts(req.result[0], c13); + checkContacts(req.result[1], c9); + checkContacts(req.result[2], c10); + checkContacts(req.result[3], c11); + checkContacts(req.result[4], c12); + next(); + }; + req.onerror = onFailure; + }, + clearDatabase, + function () { + ok(true, "Test sorting"); + createResult1 = new mozContact(c15); + req = navigator.mozContacts.save(createResult1); + req.onsuccess = function () { + ok(createResult1.id, "The contact now has an ID."); + checkContacts(c15, createResult1); + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Test sorting"); + createResult1 = new mozContact(c14); + req = navigator.mozContacts.save(createResult1); + req.onsuccess = function () { + ok(createResult1.id, "The contact now has an ID."); + checkContacts(c14, createResult1); + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Test sorting"); + createResult1 = new mozContact(c16); + req = navigator.mozContacts.save(createResult1); + req.onsuccess = function () { + ok(createResult1.id, "The contact now has an ID."); + checkContacts(c16, createResult1); + next(); + }; + req.onerror = onFailure; + }, + function () { + // Android does not support published/updated fields. Skip this. + if (isAndroid) { + next(); + return; + } + + ok(true, "Test sorting with published"); + var options = {sortBy: "phoneticFamilyName", + sortOrder: "descending"}; + req = navigator.mozContacts.find(options); + req.onsuccess = function () { + is(req.result.length, 3, "3 results"); + ok(req.result[0].published < req.result[1].published, "Right sorting order"); + ok(req.result[1].published < req.result[2].published, "Right sorting order"); + next(); + }; + req.onerror = onFailure; + }, + clearDatabase, + function () { ok(true, "all done!\n"); SimpleTest.finish(); } ]; function next() { ok(true, "Begin!"); if (index >= steps.length) {
--- a/dom/events/test/test_bug508479.html +++ b/dom/events/test/test_bug508479.html @@ -8,20 +8,17 @@ <script> var gGotHandlingDrop = false; var gGotNotHandlingDrop = false; SimpleTest.waitForExplicitFinish(); function fireEvent(target, event) { - var utils = - window.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor). - getInterface(SpecialPowers.Ci.nsIDOMWindowUtils); - SpecialPowers.wrap(utils).dispatchDOMEventViaPresShell(target, event, true); + SpecialPowers.DOMWindowUtils.dispatchDOMEventViaPresShell(target, event, true); } function fireDrop(element, shouldAllowDrop, shouldAllowOnlyChromeDrop) { var ds = SpecialPowers.Cc["@mozilla.org/widget/dragservice;1"]. getService(SpecialPowers.Ci.nsIDragService); var dataTransfer; var trapDrag = function(event) {
--- a/dom/indexedDB/test/browser_permissionsPromptAllow.js +++ b/dom/indexedDB/test/browser_permissionsPromptAllow.js @@ -5,16 +5,19 @@ const testPageURL = "http://mochi.test:8888/browser/" + "dom/indexedDB/test/browser_permissionsPrompt.html"; const notificationID = "indexedDB-permissions-prompt"; function test() { waitForExplicitFinish(); + + PopupNotifications.transitionsEnabled = false; + // We want a prompt. setPermission(testPageURL, "indexedDB", "allow"); executeSoon(test1); } function test1() { info("creating tab"); @@ -64,16 +67,17 @@ function test2() "First database creation was successful"); ok(!exception, "No exception"); is(getPermission(testPageURL, "indexedDB"), Components.interfaces.nsIPermissionManager.UNKNOWN_ACTION, "Correct permission set"); gBrowser.removeCurrentTab(); unregisterAllPopupEventHandlers(); removePermission(testPageURL, "indexedDB"); + PopupNotifications.transitionsEnabled = true; executeSoon(finish); }); registerPopupEventHandler("popupshowing", function () { ok(false, "Shouldn't show a popup this time"); }); registerPopupEventHandler("popupshown", function () { ok(false, "Shouldn't show a popup this time");
--- a/dom/indexedDB/test/browser_permissionsPromptDeny.js +++ b/dom/indexedDB/test/browser_permissionsPromptDeny.js @@ -6,16 +6,17 @@ const testPageURL = "http://mochi.test:8888/browser/" + "dom/indexedDB/test/browser_permissionsPrompt.html"; const notificationID = "indexedDB-permissions-prompt"; function test() { waitForExplicitFinish(); // We want the prompt. + PopupNotifications.transitionsEnabled = false; setPermission(testPageURL, "indexedDB", "allow"); executeSoon(test1); } function test1() { info("creating tab"); gBrowser.selectedTab = gBrowser.addTab(); @@ -63,16 +64,17 @@ function test2() ok(!result, "No database created"); is(exception, "InvalidStateError", "Correct exception"); is(getPermission(testPageURL, "indexedDB"), Components.interfaces.nsIPermissionManager.DENY_ACTION, "Correct permission set"); gBrowser.selectedBrowser.docShell.QueryInterface(Ci.nsILoadContext).usePrivateBrowsing = false; unregisterAllPopupEventHandlers(); gBrowser.removeCurrentTab(); + PopupNotifications.transitionsEnabled = true; executeSoon(test3); }); registerPopupEventHandler("popupshowing", function () { ok(false, "prompt showing"); }); registerPopupEventHandler("popupshown", function () { ok(false, "prompt shown");
--- a/dom/indexedDB/test/browser_quotaPromptAllow.js +++ b/dom/indexedDB/test/browser_quotaPromptAllow.js @@ -7,16 +7,17 @@ const testPageURL = "http://bug704464-1.example.com/browser/" + "dom/indexedDB/test/browser_quotaPrompt.html"; const notificationID = "indexedDB-quota-prompt"; function test() { waitForExplicitFinish(); requestLongerTimeout(10); + PopupNotifications.transitionsEnabled = false; removePermission(testPageURL, "indexedDB-unlimited"); Services.prefs.setIntPref("dom.indexedDB.warningQuota", 2); executeSoon(test1); } let addMoreTest1Count = 0; function test1() @@ -103,16 +104,17 @@ function test2() is(getPermission(testPageURL, "indexedDB-unlimited"), Components.interfaces.nsIPermissionManager.ALLOW_ACTION, "Correct permission set"); gBrowser.removeCurrentTab(); unregisterAllPopupEventHandlers(); removePermission(testPageURL, "indexedDB"); Services.prefs.clearUserPref("dom.indexedDB.warningQuota"); + PopupNotifications.transitionsEnabled = true; executeSoon(finish); }); executeSoon(function() { dispatchEvent("indexedDB-done"); }); } else { ++addMoreCount; executeSoon(function() { dispatchEvent("indexedDB-addMore"); }); }
--- a/dom/indexedDB/test/browser_quotaPromptDatabases.js +++ b/dom/indexedDB/test/browser_quotaPromptDatabases.js @@ -7,16 +7,17 @@ const testPageURL = "http://bug704464-3.example.com/browser/" + "dom/indexedDB/test/browser_quotaPromptDatabases.html"; const notificationID = "indexedDB-quota-prompt"; function test() { waitForExplicitFinish(); requestLongerTimeout(10); + PopupNotifications.transitionsEnabled = false; removePermission(testPageURL, "indexedDB-unlimited"); Services.prefs.setIntPref("dom.indexedDB.warningQuota", 2); executeSoon(test1); } let addMoreTest1Count = 0; function test1() @@ -38,16 +39,17 @@ function test1() setFinishedCallback(function(result) { is(result, "finished", "Got 'finished' result"); is(getPermission(testPageURL, "indexedDB-unlimited"), Components.interfaces.nsIPermissionManager.ALLOW_ACTION, "Correct permission set"); gBrowser.removeCurrentTab(); unregisterAllPopupEventHandlers(); addMoreTest1Count = seenPopupCount; + PopupNotifications.transitionsEnabled = true; executeSoon(finish); }); executeSoon(function() { dispatchEvent("indexedDB-done"); }); } else { ++addMoreTest1Count; executeSoon(function() { dispatchEvent("indexedDB-addMore"); }); }
--- a/dom/indexedDB/test/browser_quotaPromptDelete.js +++ b/dom/indexedDB/test/browser_quotaPromptDelete.js @@ -7,16 +7,17 @@ const testPageURL = "http://bug702292.example.com/browser/" + "dom/indexedDB/test/browser_quotaPromptDelete.html"; const notificationID = "indexedDB-quota-prompt"; function test() { waitForExplicitFinish(); requestLongerTimeout(10); + PopupNotifications.transitionsEnabled = false; removePermission(testPageURL, "indexedDB-unlimited"); Services.prefs.setIntPref("dom.indexedDB.warningQuota", 2); executeSoon(test1); } let addMoreTest1Count = 0; let haveReset = false; let secondTimeCount = 0; @@ -49,16 +50,17 @@ function test1() } else { setFinishedCallback(function(result) { is(result, "finished", "Got 'finished' result"); is(getPermission(testPageURL, "indexedDB-unlimited"), Components.interfaces.nsIPermissionManager.DENY_ACTION, "Correct permission set"); gBrowser.removeCurrentTab(); unregisterAllPopupEventHandlers(); + PopupNotifications.transitionsEnabled = true; executeSoon(finish); }); executeSoon(function() { dispatchEvent("indexedDB-done"); }); } } function secondTimeThroughAddMore() { setFinishedCallback(secondTimeThroughCallback);
--- a/dom/indexedDB/test/browser_quotaPromptDeny.js +++ b/dom/indexedDB/test/browser_quotaPromptDeny.js @@ -7,16 +7,17 @@ const testPageURL = "http://bug704464-2.example.com/browser/" + "dom/indexedDB/test/browser_quotaPrompt.html"; const notificationID = "indexedDB-quota-prompt"; function test() { waitForExplicitFinish(); requestLongerTimeout(10); + PopupNotifications.transitionsEnabled = false; removePermission(testPageURL, "indexedDB-unlimited"); Services.prefs.setIntPref("dom.indexedDB.warningQuota", 2); executeSoon(test1); } let addMoreTest1Count = 0; function test1() @@ -43,16 +44,17 @@ function test1() setFinishedCallback(function(result) { is(result, "finished", "Got 'finished' result"); is(getPermission(testPageURL, "indexedDB-unlimited"), Components.interfaces.nsIPermissionManager.DENY_ACTION, "Correct permission set"); gBrowser.removeCurrentTab(); unregisterAllPopupEventHandlers(); addMoreTest1Count = seenPopupCount; + PopupNotifications.transitionsEnabled = true; executeSoon(test2); }); executeSoon(function() { dispatchEvent("indexedDB-done"); }); } else { ++addMoreTest1Count; executeSoon(function() { dispatchEvent("indexedDB-addMore"); }); }
--- a/dom/tests/browser/browser_ConsoleStorageAPITests.js +++ b/dom/tests/browser/browser_ConsoleStorageAPITests.js @@ -38,18 +38,17 @@ var ConsoleObserver = { is(messages.length, 0, "Cleared Storage"); // make sure a closed window's events are in fact removed from the // storage cache win.console.log("adding a new event"); // Close the window. gBrowser.removeTab(tab, {animate: false}); // Ensure actual window destruction is not delayed (too long). - window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils).garbageCollect(); + SpecialPowers.DOMWindowUtils.garbageCollect(); // Ensure "inner-window-destroyed" event is processed, // so the storage cache is cleared. executeSoon(function () { // use the old windowID again to see if we have any stray cached messages messages = ConsoleAPIStorage.getEvents(windowID); is(messages.length, 0, "tab close is clearing the cache"); finish(); });
--- a/dom/tests/mochitest/bugs/test_bug346659.html +++ b/dom/tests/mochitest/bugs/test_bug346659.html @@ -130,20 +130,17 @@ function messageReceiver(evt) { ok(0, "unexpected test number (" + testNumber + ") when data is " + evt.data); } handleTestEnd(); } function handleTestEnd() { function gc() { - netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); - window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) - .getInterface(Components.interfaces.nsIDOMWindowUtils) - .garbageCollect(); + SpecialPowers.DOMWindowUtils.garbageCollect(); } if (numTestsSet1) { if (!--numTestsSet1) { // gc to get rid of all the old windows gc(); gc(); gc(); setTimeout(startSecondBatch, 0); } } else if (numTestsSet2) {
--- a/dom/tests/mochitest/bugs/test_bug397571.html +++ b/dom/tests/mochitest/bugs/test_bug397571.html @@ -17,18 +17,17 @@ https://bugzilla.mozilla.org/show_bug.cg </div> <pre id="test"> <script class="testbody" type="text/javascript"> /** Test for Bug 397571 **/ // Get the interface -var utils = window.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor). - getInterface(SpecialPowers.Ci.nsIDOMWindowUtils); +var utils = SpecialPowers.DOMWindowUtils.SpecialPowers_wrappedObject; // Try to call functions without privileges var success = false; try { isForced = utils.docCharsetIsForced; } catch(e) { success = true;
--- a/dom/tests/mochitest/general/file_showModalDialog.html +++ b/dom/tests/mochitest/general/file_showModalDialog.html @@ -1,15 +1,15 @@ <!DOCTYPE html> <html> <head> <script> function go() { is(SpecialPowers.wrap(window).location.toString(), location.toString(), "sanity"); - is(SpecialPowers.Cu.getClassName(window, /* aUnwrap = */ true), "ModalContentWindow", "We are modal"); + ok("returnValue" in window && "dialogArguments" in window, "We are modal"); var iwin = document.getElementById('ifr').contentWindow; is(SpecialPowers.Cu.getClassName(iwin, /* aUnwrap = */ true), "Window", "Descendant frames should not be modal"); if (location.origin != "http://mochi.test:8888") { is(window.dialogArguments, undefined, "dialogArguments should be undefined cross-origin: " + location.origin); }
--- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -1219,37 +1219,43 @@ function createInterfaceMap(isXBLScope) var isDesktop = !/Mobile|Tablet/.test(navigator.userAgent); var isB2G = !isDesktop && !navigator.userAgent.contains("Android"); var hasPermission = function (aPermission) { return SpecialPowers.hasPermission(aPermission, window.document); }; var interfaceMap = {}; - function addInterfaces(interfaces, shouldExpect) + function addInterfaces(interfaces) { for (var entry of interfaces) { if (typeof(entry) === "string") { - interfaceMap[entry] = shouldExpect; + interfaceMap[entry] = true; } else if ((entry.nightly === !isNightly) || (entry.xbl === !isXBLScope) || (entry.desktop === !isDesktop) || (entry.b2g === !isB2G) || (entry.release === !isRelease) || (entry.pref && !prefs.getBoolPref(entry.pref)) || (entry.permission && !hasPermission(entry.permission))) { interfaceMap[entry.name] = false; } else { - interfaceMap[entry.name] = shouldExpect; + interfaceMap[entry.name] = true; } } } - addInterfaces(ecmaGlobals, true); - addInterfaces(interfaceNamesInGlobalScope, true); + addInterfaces(ecmaGlobals); + addInterfaces(interfaceNamesInGlobalScope); + if (isXBLScope) { + // We expose QueryInterface to XBL scopes. It's not an interface but we + // need to handle it because it's an own property of the global and the + // property name starts with an uppercase letter. + interfaceMap["QueryInterface"] = true; + } return interfaceMap; } function runTest(isXBLScope) { var interfaceMap = createInterfaceMap(isXBLScope); for (var name of Object.getOwnPropertyNames(window)) { // An interface name should start with an upper case character.
--- a/dom/webidl/Contacts.webidl +++ b/dom/webidl/Contacts.webidl @@ -39,18 +39,20 @@ dictionary ContactProperties { sequence<ContactField>? url; sequence<ContactField>? impp; sequence<ContactTelField>? tel; sequence<DOMString>? name; sequence<DOMString>? honorificPrefix; sequence<DOMString>? givenName; + sequence<DOMString>? phoneticGivenName; sequence<DOMString>? additionalName; sequence<DOMString>? familyName; + sequence<DOMString>? phoneticFamilyName; sequence<DOMString>? honorificSuffix; sequence<DOMString>? nickname; sequence<DOMString>? category; sequence<DOMString>? org; sequence<DOMString>? jobTitle; sequence<DOMString>? note; sequence<DOMString>? key; }; @@ -76,18 +78,20 @@ interface mozContact { [Cached, Pure] attribute sequence<ContactField>? url; [Cached, Pure] attribute sequence<ContactField>? impp; [Cached, Pure] attribute sequence<ContactTelField>? tel; [Cached, Pure] attribute sequence<DOMString>? name; [Cached, Pure] attribute sequence<DOMString>? honorificPrefix; [Cached, Pure] attribute sequence<DOMString>? givenName; + [Cached, Pure] attribute sequence<DOMString>? phoneticGivenName; [Cached, Pure] attribute sequence<DOMString>? additionalName; [Cached, Pure] attribute sequence<DOMString>? familyName; + [Cached, Pure] attribute sequence<DOMString>? phoneticFamilyName; [Cached, Pure] attribute sequence<DOMString>? honorificSuffix; [Cached, Pure] attribute sequence<DOMString>? nickname; [Cached, Pure] attribute sequence<DOMString>? category; [Cached, Pure] attribute sequence<DOMString>? org; [Cached, Pure] attribute sequence<DOMString>? jobTitle; [Cached, Pure] attribute sequence<DOMString>? note; [Cached, Pure] attribute sequence<DOMString>? key;
--- a/dom/webidl/LegacyQueryInterface.webidl +++ b/dom/webidl/LegacyQueryInterface.webidl @@ -81,12 +81,13 @@ StyleSheet implements LegacyQueryInterfa Text implements LegacyQueryInterface; Touch implements LegacyQueryInterface; TouchList implements LegacyQueryInterface; TreeColumns implements LegacyQueryInterface; TreeWalker implements LegacyQueryInterface; UndoManager implements LegacyQueryInterface; ValidityState implements LegacyQueryInterface; WebSocket implements LegacyQueryInterface; +Window implements LegacyQueryInterface; XMLHttpRequest implements LegacyQueryInterface; XMLHttpRequestUpload implements LegacyQueryInterface; XMLSerializer implements LegacyQueryInterface; XPathEvaluator implements LegacyQueryInterface;
--- a/dom/webidl/Window.webidl +++ b/dom/webidl/Window.webidl @@ -11,16 +11,17 @@ * http://dev.w3.org/csswg/cssom-view/ * https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/RequestAnimationFrame/Overview.html * https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html * https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html * http://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html */ interface ApplicationCache; +interface IID; interface MozFrameRequestCallback; interface nsIBrowserDOMWindow; interface nsIMessageBroadcaster; interface nsIDOMCrypto; typedef any Transferable; // http://www.whatwg.org/specs/web-apps/current-work/ [Global, NeedNewResolve] @@ -220,16 +221,24 @@ partial interface Window { [NoInterfaceObject] interface SpeechSynthesisGetter { [Throws, Pref="media.webspeech.synth.enabled"] readonly attribute SpeechSynthesis speechSynthesis; }; Window implements SpeechSynthesisGetter; #endif +// http://www.whatwg.org/specs/web-apps/current-work/ +[NoInterfaceObject] +interface WindowModal { + [Throws, Func="nsGlobalWindow::IsModalContentWindow"] readonly attribute any dialogArguments; + [Throws, Func="nsGlobalWindow::IsModalContentWindow"] attribute any returnValue; +}; +Window implements WindowModal; + // Mozilla-specific stuff partial interface Window { //[NewObject, Throws] CSSStyleDeclaration getDefaultComputedStyle(Element elt, optional DOMString pseudoElt = ""); [NewObject, Throws] CSSStyleDeclaration? getDefaultComputedStyle(Element elt, optional DOMString pseudoElt = ""); [Throws] long mozRequestAnimationFrame(MozFrameRequestCallback aCallback); /** @@ -336,16 +345,18 @@ partial interface Window { [Throws, ChromeOnly] WindowProxy? openDialog(optional DOMString url = "", optional DOMString name = "", optional DOMString options = "", any... extraArguments); [Replaceable, Throws] readonly attribute object? content; [ChromeOnly, Throws] readonly attribute object? __content; + + [Throws, ChromeOnly] any getInterface(IID iid); }; Window implements TouchEventHandlers; Window implements OnErrorEventHandlerForWindow; // ConsoleAPI partial interface Window {
--- a/editor/composer/test/test_bug519928.html +++ b/editor/composer/test/test_bug519928.html @@ -17,17 +17,18 @@ https://bugzilla.mozilla.org/show_bug.cg <pre id="test"> <script class="testbody" type="text/javascript"> var iframe = document.getElementById("load-frame"); function enableJS() allowJS(true, iframe); function disableJS() allowJS(false, iframe); function allowJS(allow, frame) { - SpecialPowers.wrap(frame.contentWindow.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)) + SpecialPowers.wrap(frame.contentWindow) + .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor) .getInterface(SpecialPowers.Ci.nsIWebNavigation) .QueryInterface(SpecialPowers.Ci.nsIDocShell) .allowJavascript = allow; } function expectJSAllowed(allowed, testCondition, callback) { window.ICanRunMyJS = false; var self_ = window; testCondition();
--- a/editor/libeditor/html/tests/test_bug432225.html +++ b/editor/libeditor/html/tests/test_bug432225.html @@ -31,17 +31,18 @@ function getEdit() { function editDoc() { return getEdit().contentDocument; } function getSpellCheckSelection() { var Ci = SpecialPowers.Ci; var win = editDoc().defaultView; - var editingSession = SpecialPowers.wrap(win.QueryInterface(Ci.nsIInterfaceRequestor)) + var editingSession = SpecialPowers.wrap(win) + .QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIWebNavigation) .QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIEditingSession); var editor = editingSession.getEditorForWindow(win); var selcon = editor.selectionController; return selcon.getSelection(selcon.SELECTION_SPELLCHECK); }
--- a/editor/libeditor/html/tests/test_bug468353.html +++ b/editor/libeditor/html/tests/test_bug468353.html @@ -48,17 +48,18 @@ function runTest() { var editdoc = editframe.document; var editor = null; editdoc.write(''); editdoc.close(); editdoc.designMode='on'; // Hold the reference to the editor - editor = SpecialPowers.wrap(editframe.QueryInterface(Ci.nsIInterfaceRequestor)) + editor = SpecialPowers.wrap(editframe) + .QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIWebNavigation) .QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIEditingSession) .getEditorForWindow(editframe); styleSheets = editor.QueryInterface(Ci.nsIEditorStyleSheets); editdoc.designMode='off'; @@ -67,34 +68,36 @@ function runTest() { // Let go editor = null; styleSheets = null; editdoc.body.contentEditable = true; // Hold the reference to the editor - editor = SpecialPowers.wrap(editframe.QueryInterface(Ci.nsIInterfaceRequestor)) + editor = SpecialPowers.wrap(editframe) + .QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIWebNavigation) .QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIEditingSession) .getEditorForWindow(editframe); styleSheets = editor.QueryInterface(Ci.nsIEditorStyleSheets); editdoc.body.contentEditable = false; checkStylesheets(); editdoc.designMode = "on"; editdoc.body.contentEditable = true; editdoc.designMode = "off"; // Hold the reference to the editor - editor = SpecialPowers.wrap(editframe.QueryInterface(Ci.nsIInterfaceRequestor)) + editor = SpecialPowers.wrap(editframe) + .QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIWebNavigation) .QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIEditingSession) .getEditorForWindow(editframe); styleSheets = editor.QueryInterface(Ci.nsIEditorStyleSheets); editdoc.body.contentEditable = false;
--- a/gfx/2d/DataSurfaceHelpers.cpp +++ b/gfx/2d/DataSurfaceHelpers.cpp @@ -43,16 +43,39 @@ CopySurfaceDataToPackedArray(uint8_t* aS for (int row = 0; row < aSrcSize.height; ++row) { memcpy(aDst, aSrc, packedStride); aSrc += aSrcStride; aDst += packedStride; } } } +void +CopyBGRXSurfaceDataToPackedBGRArray(uint8_t* aSrc, uint8_t* aDst, + IntSize aSrcSize, int32_t aSrcStride) +{ + int packedStride = aSrcSize.width * 3; + + uint8_t* srcPx = aSrc; + uint8_t* dstPx = aDst; + + for (int row = 0; row < aSrcSize.height; ++row) { + for (int col = 0; col < aSrcSize.height; ++col) { + dstPx[0] = srcPx[0]; + dstPx[1] = srcPx[1]; + dstPx[2] = srcPx[2]; + // srcPx[3] (unused or alpha component) dropped on floor + srcPx += 4; + dstPx += 3; + } + srcPx = aSrc += aSrcStride; + dstPx = aDst += packedStride; + } +} + uint8_t* SurfaceToPackedBGRA(DataSourceSurface *aSurface) { SurfaceFormat format = aSurface->GetFormat(); if (format != SurfaceFormat::B8G8R8A8 && format != SurfaceFormat::B8G8R8X8) { return nullptr; } @@ -77,10 +100,42 @@ SurfaceToPackedBGRA(DataSourceSurface *a if (format == SurfaceFormat::B8G8R8X8) { // Convert BGRX to BGRA by setting a to 255. ConvertBGRXToBGRA(reinterpret_cast<uint8_t *>(imageBuffer), size, size.width * sizeof(uint32_t)); } return imageBuffer; } +uint8_t* +SurfaceToPackedBGR(DataSourceSurface *aSurface) +{ + SurfaceFormat format = aSurface->GetFormat(); + MOZ_ASSERT(format == SurfaceFormat::B8G8R8X8, "Format not supported"); + + if (format != SurfaceFormat::B8G8R8X8) { + // To support B8G8R8A8 we'd need to un-pre-multiply alpha + return nullptr; + } + + IntSize size = aSurface->GetSize(); + + uint8_t* imageBuffer = new (std::nothrow) uint8_t[size.width * size.height * 3 * sizeof(uint8_t)]; + if (!imageBuffer) { + return nullptr; + } + + DataSourceSurface::MappedSurface map; + if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) { + delete [] imageBuffer; + return nullptr; + } + + CopyBGRXSurfaceDataToPackedBGRArray(map.mData, imageBuffer, size, + map.mStride); + + aSurface->Unmap(); + + return imageBuffer; +} + } }
--- a/gfx/2d/DataSurfaceHelpers.h +++ b/gfx/2d/DataSurfaceHelpers.h @@ -25,10 +25,22 @@ CopySurfaceDataToPackedArray(uint8_t* aS /** * Convert aSurface to a packed buffer in BGRA format. The pixel data is * returned in a buffer allocated with new uint8_t[]. */ uint8_t* SurfaceToPackedBGRA(DataSourceSurface *aSurface); +/** + * Convert aSurface to a packed buffer in BGR format. The pixel data is + * returned in a buffer allocated with new uint8_t[]. + * + * This function is currently only intended for use with surfaces of format + * SurfaceFormat::B8G8R8X8 since the X components of the pixel data are simply + * dropped (no attempt is made to un-pre-multiply alpha from the color + * components). + */ +uint8_t* +SurfaceToPackedBGR(DataSourceSurface *aSurface); + } }
--- a/gfx/gl/GLContextProviderGLX.cpp +++ b/gfx/gl/GLContextProviderGLX.cpp @@ -10,16 +10,17 @@ #elif defined(MOZ_WIDGET_QT) #define GET_NATIVE_WINDOW(aWidget) (Window)(aWidget->GetNativeData(NS_NATIVE_SHAREABLE_WINDOW)) #endif #include <X11/Xlib.h> #include <X11/Xutil.h> #include "mozilla/MathAlgorithms.h" +#include "mozilla/StaticPtr.h" #include "mozilla/X11Util.h" #include "prenv.h" #include "GLContextProvider.h" #include "GLLibraryLoader.h" #include "nsDebug.h" #include "nsIWidget.h" #include "GLXLibrary.h" @@ -1183,34 +1184,44 @@ GLContextProviderGLX::CreateOffscreen(co return nullptr; if (!glContext->InitOffscreen(ToIntSize(size), caps)) return nullptr; return glContext.forget(); } -static nsRefPtr<GLContext> gGlobalContext; -// TODO move that out of static initializaion -static bool gUseContextSharing = getenv("MOZ_DISABLE_CONTEXT_SHARING_GLX") == 0; +static StaticRefPtr<GLContext> gGlobalContext; GLContext* GLContextProviderGLX::GetGlobalContext() { + static bool checkedContextSharing = false; + static bool useContextSharing = false; + + if (!checkedContextSharing) { + useContextSharing = getenv("MOZ_DISABLE_CONTEXT_SHARING_GLX") == 0; + checkedContextSharing = true; + } + // TODO: get GLX context sharing to work well with multiple threads - if (!gUseContextSharing) { + if (!useContextSharing) { return nullptr; } static bool triedToCreateContext = false; if (!triedToCreateContext && !gGlobalContext) { triedToCreateContext = true; gfxIntSize dummySize = gfxIntSize(16, 16); - gGlobalContext = CreateOffscreenPixmapContext(dummySize); + // StaticPtr doesn't support assignments from already_AddRefed, + // so use a temporary nsRefPtr to make the reference counting + // fall out correctly. + nsRefPtr<GLContext> holder = CreateOffscreenPixmapContext(dummySize); + gGlobalContext = holder; } return gGlobalContext; } void GLContextProviderGLX::Shutdown() {
--- a/gfx/layers/client/TextureClient.cpp +++ b/gfx/layers/client/TextureClient.cpp @@ -279,29 +279,36 @@ TextureClient::CreateTextureClientForDra const gfx::IntSize& aSizeHint) { if (aMoz2DBackend == gfx::BackendType::NONE) { aMoz2DBackend = gfxPlatform::GetPlatform()->GetContentBackend(); } RefPtr<TextureClient> result; +#if defined(MOZ_WIDGET_GONK) || defined(XP_WIN) + int32_t maxTextureSize = aAllocator->GetMaxTextureSize(); +#endif + #ifdef XP_WIN LayersBackend parentBackend = aAllocator->GetCompositorBackendType(); if (parentBackend == LayersBackend::LAYERS_D3D11 && (aMoz2DBackend == gfx::BackendType::DIRECT2D || aMoz2DBackend == gfx::BackendType::DIRECT2D1_1) && gfxWindowsPlatform::GetPlatform()->GetD2DDevice() && - + aSizeHint.width <= maxTextureSize && + aSizeHint.height <= maxTextureSize && !(aTextureFlags & TEXTURE_ALLOC_FALLBACK)) { result = new TextureClientD3D11(aFormat, aTextureFlags); } if (parentBackend == LayersBackend::LAYERS_D3D9 && aMoz2DBackend == gfx::BackendType::CAIRO && aAllocator->IsSameProcess() && + aSizeHint.width <= maxTextureSize && + aSizeHint.height <= maxTextureSize && !(aTextureFlags & TEXTURE_ALLOC_FALLBACK)) { if (!gfxWindowsPlatform::GetPlatform()->GetD3D9Device()) { result = new DIBTextureClientD3D9(aFormat, aTextureFlags); } else { result = new CairoTextureClientD3D9(aFormat, aTextureFlags); } } #endif @@ -332,17 +339,16 @@ TextureClient::CreateTextureClientForDra #endif #endif #endif #ifdef MOZ_WIDGET_GONK if (!DisableGralloc(aFormat, aSizeHint)) { // Don't allow Gralloc texture clients to exceed the maximum texture size. // BufferTextureClients have code to handle tiling the surface client-side. - int32_t maxTextureSize = aAllocator->GetMaxTextureSize(); if (aSizeHint.width <= maxTextureSize && aSizeHint.height <= maxTextureSize) { result = new GrallocTextureClientOGL(aAllocator, aFormat, aMoz2DBackend, aTextureFlags); } } #endif // Can't do any better than a buffer texture client.
--- a/gfx/layers/d3d11/TextureD3D11.cpp +++ b/gfx/layers/d3d11/TextureD3D11.cpp @@ -168,16 +168,19 @@ TextureClientD3D11::~TextureClientD3D11( UnlockD3DTexture(mTexture.get()); } #endif } bool TextureClientD3D11::Lock(OpenMode aMode) { + if (!mTexture) { + return false; + } MOZ_ASSERT(!mIsLocked, "The Texture is already locked!"); LockD3DTexture(mTexture.get()); mIsLocked = true; if (mNeedsClear) { mDrawTarget = GetAsDrawTarget(); mDrawTarget->ClearRect(Rect(0, 0, GetSize().width, GetSize().height)); mNeedsClear = false; @@ -185,16 +188,17 @@ TextureClientD3D11::Lock(OpenMode aMode) return true; } void TextureClientD3D11::Unlock() { MOZ_ASSERT(mIsLocked, "Unlocked called while the texture is not locked!"); + if (mDrawTarget) { // see the comment on TextureClientDrawTarget::GetAsDrawTarget. // This DrawTarget is internal to the TextureClient and is only exposed to the // outside world between Lock() and Unlock(). This assertion checks that no outside // reference remains by the time Unlock() is called. MOZ_ASSERT(mDrawTarget->refCount() == 1); mDrawTarget->Flush(); } @@ -205,31 +209,35 @@ TextureClientD3D11::Unlock() mIsLocked = false; } TemporaryRef<DrawTarget> TextureClientD3D11::GetAsDrawTarget() { MOZ_ASSERT(mIsLocked, "Calling TextureClient::GetAsDrawTarget without locking :("); + if (!mTexture) { + return nullptr; + } + if (mDrawTarget) { return mDrawTarget; } mDrawTarget = Factory::CreateDrawTargetForD3D10Texture(mTexture, mFormat); return mDrawTarget; } bool TextureClientD3D11::AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags aFlags) { mSize = aSize; ID3D10Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D10Device(); - CD3D10_TEXTURE2D_DESC newDesc(SurfaceFormatToDXGIFormat(mFormat), + CD3D10_TEXTURE2D_DESC newDesc(DXGI_FORMAT_B8G8R8A8_UNORM, aSize.width, aSize.height, 1, 1, D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE); newDesc.MiscFlags = D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX; HRESULT hr = device->CreateTexture2D(&newDesc, nullptr, byRef(mTexture)); if (FAILED(hr)) { @@ -327,20 +335,20 @@ DXGITextureHostD3D11::GetTextureSources( return mTextureSource.get(); } bool DataTextureSourceD3D11::Update(DataSourceSurface* aSurface, nsIntRegion* aDestRegion, IntPoint* aSrcOffset) { - // Right now we only support null aDestRegion and aSrcOffset (which means) - // full surface update. Incremental update is only used on Mac so it is - // not clear that we ever will need to support it for D3D. - MOZ_ASSERT(!aDestRegion && !aSrcOffset); + // Right now we only support full surface update. If aDestRegion is provided, + // It will be ignored. Incremental update with a source offset is only used + // on Mac so it is not clear that we ever will need to support it for D3D. + MOZ_ASSERT(!aSrcOffset); MOZ_ASSERT(aSurface); if (!mCompositor || !mCompositor->GetDevice()) { return false; } uint32_t bpp = BytesPerPixel(aSurface->GetFormat()); DXGI_FORMAT dxgiFormat = SurfaceFormatToDXGIFormat(aSurface->GetFormat());
--- a/gfx/layers/d3d9/TextureD3D9.cpp +++ b/gfx/layers/d3d9/TextureD3D9.cpp @@ -371,20 +371,20 @@ DataTextureSourceD3D9::GetD3D9Texture() : mTexture; } bool DataTextureSourceD3D9::Update(gfx::DataSourceSurface* aSurface, nsIntRegion* aDestRegion, gfx::IntPoint* aSrcOffset) { - // Right now we only support null aDestRegion and aSrcOffset (which means - // full surface update). Incremental update is only used on Mac so it is - // not clear that we ever will need to support it for D3D. - MOZ_ASSERT(!aDestRegion && !aSrcOffset); + // Right now we only support full surface update. If aDestRegion is provided, + // It will be ignored. Incremental update with a source offset is only used + // on Mac so it is not clear that we ever will need to support it for D3D. + MOZ_ASSERT(!aSrcOffset); if (!mCompositor || !mCompositor->device()) { NS_WARNING("No D3D device to update the texture."); return false; } mSize = aSurface->GetSize(); uint32_t bpp = 0;
--- a/gfx/layers/opengl/GrallocTextureHost.cpp +++ b/gfx/layers/opengl/GrallocTextureHost.cpp @@ -389,17 +389,16 @@ GrallocTextureHostOGL::GetRenderState() TemporaryRef<gfx::DataSourceSurface> GrallocTextureHostOGL::GetAsSurface() { return mTextureSource ? mTextureSource->GetAsSurface() : nullptr; } TemporaryRef<gfx::DataSourceSurface> GrallocTextureSourceOGL::GetAsSurface() { - MOZ_ASSERT(gl()); if (!IsValid()) { return nullptr; } gl()->MakeCurrent(); GLuint tex = GetGLTexture(); gl()->fActiveTexture(LOCAL_GL_TEXTURE0); gl()->fBindTexture(GetTextureTarget(), tex);
--- a/gfx/tests/mochitest/test_bug513439.html +++ b/gfx/tests/mochitest/test_bug513439.html @@ -20,18 +20,17 @@ https://bugzilla.mozilla.org/show_bug.cg /** Test for Bug 513439 **/ var prefService = SpecialPowers.Cc["@mozilla.org/preferences-service;1"] .getService(SpecialPowers.Ci.nsIPrefService); var layoutCSSBranch = prefService.getBranch("layout.css."); var oldVal = layoutCSSBranch.getCharPref("devPixelsPerPx"); try { - var domWindowUtils = window.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor) - .getInterface(SpecialPowers.Ci.nsIDOMWindowUtils); + var domWindowUtils = SpecialPowers.DOMWindowUtils; var devPxPerCSSPx = domWindowUtils.screenPixelsPerCSSPixel; layoutCSSBranch.setCharPref("devPixelsPerPx", "2"); is(domWindowUtils.screenPixelsPerCSSPixel, 2, "devPixelsPerPx wasn't set correctly"); layoutCSSBranch.setCharPref("devPixelsPerPx", "1.5"); is(domWindowUtils.screenPixelsPerCSSPixel, 1.5, "devPixelsPerPx wasn't set correctly");
--- a/gfx/thebes/gfxAndroidPlatform.cpp +++ b/gfx/thebes/gfxAndroidPlatform.cpp @@ -346,16 +346,24 @@ gfxFontEntry* gfxAndroidPlatform::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry, const uint8_t *aFontData, uint32_t aLength) { return gfxPlatformFontList::PlatformFontList()->MakePlatformFont(aProxyEntry, aFontData, aLength); } +gfxFontEntry* +gfxAndroidPlatform::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry, + const nsAString& aFontName) +{ + return gfxPlatformFontList::PlatformFontList()->LookupLocalFont(aProxyEntry, + aFontName); +} + TemporaryRef<ScaledFont> gfxAndroidPlatform::GetScaledFontForFont(DrawTarget* aTarget, gfxFont *aFont) { return GetScaledFontForFontWithCairoSkia(aTarget, aFont); } bool gfxAndroidPlatform::FontHintingEnabled()
--- a/gfx/thebes/gfxAndroidPlatform.h +++ b/gfx/thebes/gfxAndroidPlatform.h @@ -44,16 +44,18 @@ public: // to support IPC font list (sharing between chrome and content) void GetFontList(InfallibleTArray<FontListEntry>* retValue); // platform implementations of font functions virtual bool IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags); virtual gfxPlatformFontList* CreatePlatformFontList(); virtual gfxFontEntry* MakePlatformFont(const gfxProxyFontEntry *aProxyEntry, const uint8_t *aFontData, uint32_t aLength); + virtual gfxFontEntry* LookupLocalFont(const gfxProxyFontEntry *aProxyEntry, + const nsAString& aFontName); virtual void GetCommonFallbackFonts(const uint32_t aCh, int32_t aRunScript, nsTArray<const char*>& aFontList); virtual nsresult GetFontList(nsIAtom *aLangGroup, const nsACString& aGenericFamily, nsTArray<nsString>& aListOfFonts);
--- a/gfx/thebes/gfxFT2FontList.cpp +++ b/gfx/thebes/gfxFT2FontList.cpp @@ -1381,41 +1381,46 @@ gfxFT2FontList::InitFontList() } struct FullFontNameSearch { FullFontNameSearch(const nsAString& aFullName) : mFullName(aFullName), mFontEntry(nullptr) { } nsString mFullName; - gfxFontEntry *mFontEntry; + FT2FontEntry *mFontEntry; }; // callback called for each family name, based on the assumption that the // first part of the full name is the family name static PLDHashOperator FindFullName(nsStringHashKey::KeyType aKey, nsRefPtr<gfxFontFamily>& aFontFamily, void* userArg) { FullFontNameSearch *data = reinterpret_cast<FullFontNameSearch*>(userArg); // does the family name match up to the length of the family name? const nsString& family = aFontFamily->Name(); - + nsString fullNameFamily; data->mFullName.Left(fullNameFamily, family.Length()); // if so, iterate over faces in this family to see if there is a match - if (family.Equals(fullNameFamily)) { + if (family.Equals(fullNameFamily, nsCaseInsensitiveStringComparator())) { nsTArray<nsRefPtr<gfxFontEntry> >& fontList = aFontFamily->GetFontList(); int index, len = fontList.Length(); for (index = 0; index < len; index++) { - if (fontList[index]->Name().Equals(data->mFullName)) { - data->mFontEntry = fontList[index]; + gfxFontEntry* fe = fontList[index]; + if (!fe) { + continue; + } + if (fe->Name().Equals(data->mFullName, + nsCaseInsensitiveStringComparator())) { + data->mFontEntry = static_cast<FT2FontEntry*>(fe); return PL_DHASH_STOP; } } } return PL_DHASH_NEXT; } @@ -1423,17 +1428,42 @@ gfxFontEntry* gfxFT2FontList::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry, const nsAString& aFontName) { // walk over list of names FullFontNameSearch data(aFontName); mFontFamilies.Enumerate(FindFullName, &data); - return data.mFontEntry; + if (!data.mFontEntry) { + return nullptr; + } + + // Clone the font entry so that we can then set its style descriptors + // from the proxy rather than the actual font. + + // Ensure existence of mFTFace in the original entry + data.mFontEntry->CairoFontFace(); + if (!data.mFontEntry->mFTFace) { + return nullptr; + } + + FT2FontEntry* fe = + FT2FontEntry::CreateFontEntry(data.mFontEntry->mFTFace, + data.mFontEntry->mFilename.get(), + data.mFontEntry->mFTFontIndex, + data.mFontEntry->Name(), nullptr); + if (fe) { + fe->mItalic = aProxyEntry->mItalic; + fe->mWeight = aProxyEntry->mWeight; + fe->mStretch = aProxyEntry->mStretch; + fe->mIsUserFont = fe->mIsLocalUserFont = true; + } + + return fe; } gfxFontFamily* gfxFT2FontList::GetDefaultFont(const gfxFontStyle* aStyle) { #ifdef XP_WIN HGDIOBJ hGDI = ::GetStockObject(SYSTEM_FONT); LOGFONTW logFont;
--- a/gfx/thebes/gfxUtils.cpp +++ b/gfx/thebes/gfxUtils.cpp @@ -2,16 +2,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/. */ #include "gfxUtils.h" #include "gfxContext.h" #include "gfxPlatform.h" #include "gfxDrawable.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/RefPtr.h" #include "nsRegion.h" #include "yuv_convert.h" #include "ycbcr_to_rgb565.h" #include "GeckoProfiler.h" #include "ImageContainer.h" #include "gfx2DGlue.h" #ifdef XP_WIN @@ -843,16 +845,86 @@ gfxUtils::ConvertYCbCrToRGB(const Planar aData.mPicSize.height, aData.mYStride, aData.mCbCrStride, aStride, yuvtype); } } +/* static */ TemporaryRef<DataSourceSurface> +gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat(SourceSurface* aSurface, + SurfaceFormat aFormat) +{ + MOZ_ASSERT(aFormat != aSurface->GetFormat(), + "Unnecessary - and very expersive - surface format conversion"); + + Rect bounds(0, 0, aSurface->GetSize().width, aSurface->GetSize().height); + + if (aSurface->GetType() != SurfaceType::DATA) { + // If the surface is NOT of type DATA then its data is not mapped into main + // memory. Format conversion is probably faster on the GPU, and by doing it + // there we can avoid any expensive uploads/readbacks except for (possibly) + // a single readback due to the unavoidable GetDataSurface() call. Using + // CreateOffscreenContentDrawTarget ensures the conversion happens on the + // GPU. + RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()-> + CreateOffscreenContentDrawTarget(aSurface->GetSize(), aFormat); + // Using DrawSurface() here rather than CopySurface() because CopySurface + // is optimized for memcpy and therefore isn't good for format conversion. + // Using OP_OVER since in our case it's equivalent to OP_SOURCE and + // generally more optimized. + dt->DrawSurface(aSurface, bounds, bounds, DrawSurfaceOptions(), + DrawOptions(1.0f, CompositionOp::OP_OVER)); + RefPtr<SourceSurface> surface = dt->Snapshot(); + return surface->GetDataSurface(); + } + + // If the surface IS of type DATA then it may or may not be in main memory + // depending on whether or not it has been mapped yet. We have no way of + // knowing, so we can't be sure if it's best to create a data wrapping + // DrawTarget for the conversion or an offscreen content DrawTarget. We could + // guess it's not mapped and create an offscreen content DrawTarget, but if + // it is then we'll end up uploading the surface data, and most likely the + // caller is going to be accessing the resulting surface data, resulting in a + // readback (both very expensive operations). Alternatively we could guess + // the data is mapped and create a data wrapping DrawTarget and, if the + // surface is not in main memory, then we will incure a readback. The latter + // of these two "wrong choices" is the least costly (a readback, vs an + // upload and a readback), and more than likely the DATA surface that we've + // been passed actually IS in main memory anyway. For these reasons it's most + // likely best to create a data wrapping DrawTarget here to do the format + // conversion. + RefPtr<DataSourceSurface> dataSurface = + Factory::CreateDataSourceSurface(aSurface->GetSize(), aFormat); + DataSourceSurface::MappedSurface map; + if (!dataSurface || + !dataSurface->Map(DataSourceSurface::MapType::READ_WRITE, &map)) { + return nullptr; + } + RefPtr<DrawTarget> dt = + Factory::CreateDrawTargetForData(BackendType::CAIRO, + map.mData, + dataSurface->GetSize(), + map.mStride, + aFormat); + if (!dt) { + dataSurface->Unmap(); + return nullptr; + } + // Using DrawSurface() here rather than CopySurface() because CopySurface + // is optimized for memcpy and therefore isn't good for format conversion. + // Using OP_OVER since in our case it's equivalent to OP_SOURCE and + // generally more optimized. + dt->DrawSurface(aSurface, bounds, bounds, DrawSurfaceOptions(), + DrawOptions(1.0f, CompositionOp::OP_OVER)); + dataSurface->Unmap(); + return dataSurface.forget(); +} + #ifdef MOZ_DUMP_PAINTING /* static */ void gfxUtils::WriteAsPNG(DrawTarget* aDT, const char* aFile) { aDT->Flush(); nsRefPtr<gfxASurface> surf = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(aDT); if (surf) { surf->WriteAsPNG(aFile);
--- a/gfx/thebes/gfxUtils.h +++ b/gfx/thebes/gfxUtils.h @@ -4,31 +4,37 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef GFX_UTILS_H #define GFX_UTILS_H #include "gfxTypes.h" #include "GraphicsFilter.h" #include "imgIContainer.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/RefPtr.h" class gfxDrawable; class nsIntRegion; struct nsIntRect; namespace mozilla { namespace layers { class PlanarYCbCrData; } } class gfxUtils { public: + typedef mozilla::gfx::DataSourceSurface DataSourceSurface; typedef mozilla::gfx::IntPoint IntPoint; typedef mozilla::gfx::Matrix Matrix; + typedef mozilla::gfx::SourceSurface SourceSurface; + typedef mozilla::gfx::SurfaceFormat SurfaceFormat; + /* * Premultiply or Unpremultiply aSourceSurface, writing the result * to aDestSurface or back into aSourceSurface if aDestSurface is null. * * If aDestSurface is given, it must have identical format, dimensions, and * stride as the source. * * If the source is not gfxImageFormat::ARGB32, no operation is performed. If @@ -150,16 +156,61 @@ public: */ static void ConvertYCbCrToRGB(const mozilla::layers::PlanarYCbCrData& aData, const gfxImageFormat& aDestFormat, const gfxIntSize& aDestSize, unsigned char* aDestBuffer, int32_t aStride); + /** + * Creates a copy of aSurface, but having the SurfaceFormat aFormat. + * + * This function always creates a new surface. Do not call it if aSurface's + * format is the same as aFormat. Such a non-conversion would just be an + * unnecessary and wasteful copy (this function asserts to prevent that). + * + * This function is intended to be called by code that needs to access the + * pixel data of the surface, but doesn't want to have lots of branches + * to handle different pixel data formats (code which would become out of + * date if and when new formats are added). Callers can use this function + * to copy the surface to a specified format so that they only have to + * handle pixel data in that one format. + * + * WARNING: There are format conversions that will not be supported by this + * function. It very much depends on what the Moz2D backends support. If + * the temporary B8G8R8A8 DrawTarget that this function creates has a + * backend that supports DrawSurface() calls passing a surface with + * aSurface's format it will work. Otherwise it will not. + * + * *** IMPORTANT PERF NOTE *** + * + * This function exists partly because format conversion is fraught with + * non-obvious performance hazards, so we don't want Moz2D consumers to be + * doing their own format conversion. Do not try to do so, or at least read + * the comments in this functions implemtation. That said, the copy that + * this function carries out has a cost and, although this function tries + * to avoid perf hazards such as expensive uploads to/readbacks from the + * GPU, it can't guarantee that it always successfully does so. Perf + * critical code that can directly handle the common formats that it + * encounters in a way that is cheaper than a copy-with-format-conversion + * should consider doing so, and only use this function as a fallback to + * handle other formats. + * + * XXXjwatt it would be nice if SourceSurface::GetDataSurface took a + * SurfaceFormat argument (with a default argument meaning "use the + * existing surface's format") and returned a DataSourceSurface in that + * format. (There would then be an issue of callers maybe failing to + * realize format conversion may involve expensive copying/uploading/ + * readback.) + */ + static mozilla::TemporaryRef<DataSourceSurface> + CopySurfaceToDataSourceSurfaceWithFormat(SourceSurface* aSurface, + SurfaceFormat aFormat); + static const uint8_t sUnpremultiplyTable[256*256]; static const uint8_t sPremultiplyTable[256*256]; #ifdef MOZ_DUMP_PAINTING /** * Writes a binary PNG file. */ static void WriteAsPNG(mozilla::gfx::DrawTarget* aDT, const char* aFile);
--- a/gfx/thebes/moz.build +++ b/gfx/thebes/moz.build @@ -62,49 +62,46 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'andr ] SOURCES += [ 'gfxAndroidPlatform.cpp', 'gfxFT2FontBase.cpp', 'gfxFT2FontList.cpp', 'gfxFT2Fonts.cpp', 'gfxFT2Utils.cpp', 'gfxPDFSurface.cpp', - 'nsUnicodeRange.cpp', ] elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk': EXPORTS += [ 'gfxAndroidPlatform.h', 'gfxFT2FontBase.h', 'gfxFT2Fonts.h', 'gfxPDFSurface.h', ] SOURCES += [ 'gfxAndroidPlatform.cpp', 'gfxFT2FontBase.cpp', 'gfxFT2FontList.cpp', 'gfxFT2Fonts.cpp', 'gfxFT2Utils.cpp', 'gfxPDFSurface.cpp', - 'nsUnicodeRange.cpp', ] elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': EXPORTS += [ 'gfxPlatformMac.h', 'gfxQuartzImageSurface.h', 'gfxQuartzNativeDrawing.h', 'gfxQuartzSurface.h', ] SOURCES += [ 'gfxCoreTextShaper.cpp', 'gfxMacFont.cpp', 'gfxPlatformMac.cpp', 'gfxQuartzImageSurface.cpp', 'gfxQuartzNativeDrawing.cpp', 'gfxQuartzSurface.cpp', - 'nsUnicodeRange.cpp', ] elif CONFIG['MOZ_WIDGET_GTK']: EXPORTS += [ 'gfxFT2FontBase.h', 'gfxGdkNativeRenderer.h', 'gfxPangoFonts.h', 'gfxPDFSurface.h', 'gfxPlatformGtk.h', @@ -115,17 +112,16 @@ elif CONFIG['MOZ_WIDGET_GTK']: 'gfxFontconfigUtils.cpp', 'gfxFT2FontBase.cpp', 'gfxFT2Utils.cpp', 'gfxGdkNativeRenderer.cpp', 'gfxPangoFonts.cpp', 'gfxPDFSurface.cpp', 'gfxPlatformGtk.cpp', 'gfxPSSurface.cpp', - 'nsUnicodeRange.cpp', ] if CONFIG['MOZ_X11']: EXPORTS += [ 'gfxXlibNativeRenderer.h', 'gfxXlibSurface.h', ] SOURCES += [ @@ -145,17 +141,16 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'qt SOURCES += [ 'gfxFontconfigUtils.cpp', 'gfxFT2FontBase.cpp', 'gfxFT2Utils.cpp', 'gfxPangoFonts.cpp', 'gfxPDFSurface.cpp', 'gfxQPainterSurface.cpp', 'gfxQtPlatform.cpp', - 'nsUnicodeRange.cpp', ] if CONFIG['MOZ_X11']: EXPORTS += [ 'gfxXlibSurface.h', ] SOURCES += [ 'gfxQtNativeRenderer.cpp', @@ -179,17 +174,16 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'wi 'gfxGDIFont.cpp', 'gfxGDIFontList.cpp', 'gfxGDIShaper.cpp', 'gfxPDFSurface.cpp', 'gfxUniscribeShaper.cpp', 'gfxWindowsNativeDrawing.cpp', 'gfxWindowsPlatform.cpp', 'gfxWindowsSurface.cpp', - 'nsUnicodeRange.cpp', ] if CONFIG['MOZ_ENABLE_DWRITE_FONT']: # gfxDWriteFontList.cpp forces NSPR logging, so it cannot be built in unified mode. SOURCES += [ 'gfxD2DSurface.cpp', 'gfxDWriteCommon.cpp', 'gfxDWriteFontList.cpp', 'gfxDWriteFonts.cpp', @@ -248,16 +242,17 @@ UNIFIED_SOURCES += [ 'gfxReusableImageSurfaceWrapper.cpp', 'gfxReusableSharedImageSurfaceWrapper.cpp', 'gfxScriptItemizer.cpp', 'gfxSkipChars.cpp', 'gfxSVGGlyphs.cpp', 'gfxTeeSurface.cpp', 'gfxUtils.cpp', 'nsSurfaceTexture.cpp', + 'nsUnicodeRange.cpp', ] if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': # gfxMacPlatformFontList.mm forces NSPR logging so it cannot be built in unified mode. SOURCES += [ 'gfxMacPlatformFontList.mm', ]
--- a/intl/uconv/ucvlatin/nsUTF16ToUnicode.cpp +++ b/intl/uconv/ucvlatin/nsUTF16ToUnicode.cpp @@ -1,15 +1,16 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* 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 "nsUTF16ToUnicode.h" #include "nsCharTraits.h" +#include "mozilla/Endian.h" enum { STATE_NORMAL = 0, STATE_HALF_CODE_POINT = 1, STATE_FIRST_CALL = 2, STATE_SECOND_BYTE = STATE_FIRST_CALL | STATE_HALF_CODE_POINT, STATE_ODD_SURROGATE_PAIR = 4 }; @@ -70,17 +71,17 @@ nsUTF16ToUnicodeBase::UTF16ConvertToUnic char16_t u; if (mState == STATE_HALF_CODE_POINT) { if (dest == destEnd) goto error; // the 1st byte of a 16-bit code unit was stored in |mOddByte| in the // previous run while the 2nd byte has to come from |*src|. mState = STATE_NORMAL; -#ifdef IS_BIG_ENDIAN +#if MOZ_BIG_ENDIAN u = (mOddByte << 8) | uint8_t(*src++); // safe, we know we have at least one byte. #else u = (*src++ << 8) | mOddByte; // safe, we know we have at least one byte. #endif srcEvenEnd = src + ((srcEnd - src) & ~1); // handle even number of bytes in main loop goto have_codepoint; } else { srcEvenEnd = src + ((srcEnd - src) & ~1); // handle even number of bytes in main loop @@ -204,17 +205,17 @@ nsUTF16BEToUnicode::Convert(const char * if (uint8_t(*aSrc) != 0xFE) { mState = STATE_NORMAL; break; } *aDestLength = 0; mState = STATE_SECOND_BYTE; return NS_OK_UDEC_MOREINPUT; } -#ifdef IS_LITTLE_ENDIAN +#if MOZ_LITTLE_ENDIAN // on LE machines, BE BOM is 0xFFFE if (0xFFFE != *((char16_t*)aSrc)) { mState = STATE_NORMAL; } #else if (0xFEFF != *((char16_t*)aSrc)) { mState = STATE_NORMAL; } @@ -228,24 +229,18 @@ nsUTF16BEToUnicode::Convert(const char * } if (uint8_t(*aSrc) != 0xFF) { mOddByte = 0xFE; mState = STATE_HALF_CODE_POINT; } break; } - nsresult rv = UTF16ConvertToUnicode(aSrc, aSrcLength, aDest, aDestLength, -#ifdef IS_LITTLE_ENDIAN - true -#else - false -#endif - ); - return rv; + return UTF16ConvertToUnicode(aSrc, aSrcLength, aDest, aDestLength, + bool(MOZ_LITTLE_ENDIAN)); } NS_IMETHODIMP nsUTF16LEToUnicode::Convert(const char * aSrc, int32_t * aSrcLength, char16_t * aDest, int32_t * aDestLength) { switch (mState) { case STATE_FIRST_CALL: @@ -257,17 +252,17 @@ nsUTF16LEToUnicode::Convert(const char * if (uint8_t(*aSrc) != 0xFF) { mState = STATE_NORMAL; break; } *aDestLength = 0; mState = STATE_SECOND_BYTE; return NS_OK_UDEC_MOREINPUT; } -#ifdef IS_BIG_ENDIAN +#if MOZ_BIG_ENDIAN // on BE machines, LE BOM is 0xFFFE if (0xFFFE != *((char16_t*)aSrc)) { mState = STATE_NORMAL; } #else if (0xFEFF != *((char16_t*)aSrc)) { mState = STATE_NORMAL; } @@ -281,24 +276,18 @@ nsUTF16LEToUnicode::Convert(const char * } if (uint8_t(*aSrc) != 0xFE) { mOddByte = 0xFF; mState = STATE_HALF_CODE_POINT; } break; } - nsresult rv = UTF16ConvertToUnicode(aSrc, aSrcLength, aDest, aDestLength, -#ifdef IS_BIG_ENDIAN - true -#else - false -#endif - ); - return rv; + return UTF16ConvertToUnicode(aSrc, aSrcLength, aDest, aDestLength, + bool(MOZ_BIG_ENDIAN)); } NS_IMETHODIMP nsUTF16ToUnicode::Reset() { mEndian = kUnknown; mFoundBOM = false; return nsUTF16ToUnicodeBase::Reset(); @@ -343,21 +332,19 @@ nsUTF16ToUnicode::Convert(const char * a // and let the garbage show up in the browser. (security concern?) // (bug 246194) mState = STATE_NORMAL; mEndian = kBigEndian; } } nsresult rv = UTF16ConvertToUnicode(aSrc, aSrcLength, aDest, aDestLength, -#ifdef IS_BIG_ENDIAN +#if MOZ_BIG_ENDIAN (mEndian == kLittleEndian) -#elif defined(IS_LITTLE_ENDIAN) +#else (mEndian == kBigEndian) -#else - #error "Unknown endianness" #endif ); // If BOM is not found and we're to return NS_OK, signal that BOM // is not found. Otherwise, return |rv| from |UTF16ConvertToUnicode| return (rv == NS_OK && !mFoundBOM) ? NS_OK_UDEC_NOBOMFOUND : rv; }
--- a/intl/uconv/ucvlatin/nsUnicodeToUTF16.cpp +++ b/intl/uconv/ucvlatin/nsUnicodeToUTF16.cpp @@ -1,18 +1,16 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* 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 "nsUnicodeToUTF16.h" #include <string.h> -inline static void SwapBytes(char *aDest, const char16_t* aSrc, int32_t aLen); - NS_IMETHODIMP nsUnicodeToUTF16BE::Convert(const char16_t * aSrc, int32_t * aSrcLength, char * aDest, int32_t * aDestLength) { int32_t srcInLen = *aSrcLength; int32_t destInLen = *aDestLength; int32_t srcOutLen = 0; int32_t destOutLen = 0; int32_t copyCharLen; @@ -92,45 +90,17 @@ NS_IMETHODIMP nsUnicodeToUTF16BE::Reset( NS_IMETHODIMP nsUnicodeToUTF16BE::SetOutputErrorBehavior(int32_t aBehavior, nsIUnicharEncoder * aEncoder, char16_t aChar) { return NS_OK; } NS_IMETHODIMP nsUnicodeToUTF16BE::CopyData(char* aDest, const char16_t* aSrc, int32_t aLen ) { -#ifdef IS_BIG_ENDIAN - //UnicodeToUTF16SameEndian - ::memcpy(aDest, (void*) aSrc, aLen * 2); -#elif defined(IS_LITTLE_ENDIAN) - //UnicodeToUTF16DiffEndian - SwapBytes(aDest, aSrc, aLen); -#else - #error "Unknown endianness" -#endif + mozilla::NativeEndian::copyAndSwapToBigEndian(aDest, aSrc, aLen); return NS_OK; } NS_IMETHODIMP nsUnicodeToUTF16LE::CopyData(char* aDest, const char16_t* aSrc, int32_t aLen ) { -#ifdef IS_LITTLE_ENDIAN - //UnicodeToUTF16SameEndian - ::memcpy(aDest, (void*) aSrc, aLen * 2); -#elif defined(IS_BIG_ENDIAN) - //UnicodeToUTF16DiffEndian - SwapBytes(aDest, aSrc, aLen); -#else - #error "Unknown endianness" -#endif + mozilla::NativeEndian::copyAndSwapToLittleEndian(aDest, aSrc, aLen); return NS_OK; } - -inline void SwapBytes(char *aDest, const char16_t* aSrc, int32_t aLen) -{ - char16_t *p = (char16_t*) aDest; - // copy the data by swaping - for(int32_t i = 0; i < aLen; i++) - { - char16_t aChar = *aSrc++; - *p++ = (0x00FF & (aChar >> 8)) | (0xFF00 & (aChar << 8)); - } -} -
--- a/intl/uconv/ucvlatin/nsUnicodeToUTF16.h +++ b/intl/uconv/ucvlatin/nsUnicodeToUTF16.h @@ -2,16 +2,17 @@ /* 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 nsUnicodeToUTF16_h_ #define nsUnicodeToUTF16_h_ #include "nsUCSupport.h" +#include "mozilla/Endian.h" class nsUnicodeToUTF16BE: public nsBasicEncoder { public: nsUnicodeToUTF16BE() { mBOM = 0;} //-------------------------------------------------------------------- // Interface nsIUnicodeEncoder [declaration] @@ -34,23 +35,19 @@ class nsUnicodeToUTF16LE: public nsUnico { public: nsUnicodeToUTF16LE() { mBOM = 0;} protected: NS_IMETHOD CopyData(char* aDest, const char16_t* aSrc, int32_t aLen ); }; -// XXX In theory, we have to check the endianness at run-time because some -// modern RISC processors can be run at both LE and BE. -#ifdef IS_LITTLE_ENDIAN +#if MOZ_LITTLE_ENDIAN class nsUnicodeToUTF16: public nsUnicodeToUTF16LE -#elif defined(IS_BIG_ENDIAN) +#else class nsUnicodeToUTF16: public nsUnicodeToUTF16BE -#else -#error "Unknown endianness" #endif { public: nsUnicodeToUTF16() { mBOM = 0xFEFF;} }; #endif /* nsUnicodeToUTF16_h_ */
--- a/js/public/ProfilingStack.h +++ b/js/public/ProfilingStack.h @@ -30,19 +30,19 @@ class ProfileEntry // // entry[size] = ...; // size++; // // If the size modification were somehow reordered before the stores, then // if a sample were taken it would be examining bogus information. // // A ProfileEntry represents both a C++ profile entry and a JS one. Both use - // the string as a description, but JS uses the sp as nullptr to indicate - // that it is a JS entry. The script_ is then only ever examined for a JS - // entry, and the idx is used by both, but with different meanings. + // the string as a description, but JS uses the sp as nullptr or (void*)1 to + // indicate that it is a JS entry. The script_ is then only ever examined for + // a JS entry, and the idx is used by both, but with different meanings. // const char * volatile string; // Descriptive string of this entry void * volatile sp; // Relevant stack pointer for the entry JSScript * volatile script_; // if js(), non-null script which is running int32_t volatile idx; // if js(), idx of pc, otherwise line number public: // All of these methods are marked with the 'volatile' keyword because SPS's
--- a/js/public/RootingAPI.h +++ b/js/public/RootingAPI.h @@ -240,16 +240,28 @@ class Heap : public js::HeapBase<T> } else if (js::GCMethods<T>::needsPostBarrier(ptr)) { relocate(); /* Called before overwriting ptr. */ ptr = newPtr; } else { ptr = newPtr; } } + /* + * Set the pointer to a value which will cause a crash if it is + * dereferenced. + */ + void setToCrashOnTouch() { + ptr = reinterpret_cast<T>(crashOnTouchPointer); + } + + bool isSetToCrashOnTouch() { + return ptr == crashOnTouchPointer; + } + private: void init(T newPtr) { MOZ_ASSERT(!js::GCMethods<T>::poisoned(newPtr)); ptr = newPtr; if (js::GCMethods<T>::needsPostBarrier(ptr)) post(); } @@ -261,16 +273,20 @@ class Heap : public js::HeapBase<T> } void relocate() { #ifdef JSGC_GENERATIONAL js::GCMethods<T>::relocate(&ptr); #endif } + enum { + crashOnTouchPointer = 1 + }; + T ptr; }; #ifdef JS_DEBUG /* * For generational GC, assert that an object is in the tenured generation as * opposed to being in the nursery. */ @@ -358,33 +374,20 @@ class TenuredHeap : public js::HeapBase< return *this; } TenuredHeap<T> &operator=(const TenuredHeap<T>& other) { bits = other.bits; return *this; } - /* - * Set the pointer to a value which will cause a crash if it is - * dereferenced. - */ - void setToCrashOnTouch() { - bits = (bits & flagsMask) | crashOnTouchPointer; - } - - bool isSetToCrashOnTouch() { - return (bits & ~flagsMask) == crashOnTouchPointer; - } - private: enum { maskBits = 3, flagsMask = (1 << maskBits) - 1, - crashOnTouchPointer = 1 << maskBits }; uintptr_t bits; }; /* * Reference to a T that has been rooted elsewhere. This is most useful * as a parameter type, which guarantees that the T lvalue is properly
--- a/js/src/jit/BaselineBailouts.cpp +++ b/js/src/jit/BaselineBailouts.cpp @@ -5,16 +5,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "jsprf.h" #include "jit/arm/Simulator-arm.h" #include "jit/BaselineIC.h" #include "jit/BaselineJIT.h" #include "jit/CompileInfo.h" #include "jit/IonSpewer.h" +#include "jit/Recover.h" #include "vm/ArgumentsObject.h" #include "jsscriptinlines.h" #include "jit/IonFrames-inl.h" using namespace js; using namespace js::jit; @@ -471,17 +472,17 @@ InitFromBailout(JSContext *cx, HandleScr { // If excInfo is non-nullptr, we are bailing out to a catch or finally block // and this is the frame where we will resume. Usually the expression stack // should be empty in this case but there can be iterators on the stack. uint32_t exprStackSlots; if (excInfo) exprStackSlots = excInfo->numExprSlots; else - exprStackSlots = iter.allocations() - (script->nfixed() + CountArgSlots(script, fun)); + exprStackSlots = iter.numAllocations() - (script->nfixed() + CountArgSlots(script, fun)); builder.resetFramePushed(); // Build first baseline frame: // +===============+ // | PrevFramePtr | // +---------------+ // | Baseline | @@ -624,19 +625,19 @@ InitFromBailout(JSContext *cx, HandleScr // in the calling frame. Value thisv = iter.read(); IonSpew(IonSpew_BaselineBailouts, " Is function!"); IonSpew(IonSpew_BaselineBailouts, " thisv=%016llx", *((uint64_t *) &thisv)); size_t thisvOffset = builder.framePushed() + IonJSFrameLayout::offsetOfThis(); *builder.valuePointerAtStackOffset(thisvOffset) = thisv; - JS_ASSERT(iter.allocations() >= CountArgSlots(script, fun)); + JS_ASSERT(iter.numAllocations() >= CountArgSlots(script, fun)); IonSpew(IonSpew_BaselineBailouts, " frame slots %u, nargs %u, nfixed %u", - iter.allocations(), fun->nargs(), script->nfixed()); + iter.numAllocations(), fun->nargs(), script->nfixed()); if (!callerPC) { // This is the first frame. Store the formals in a Vector until we // are done. Due to UCE and phi elimination, we could store an // UndefinedValue() here for formals we think are unused, but // locals may still reference the original argument slot // (MParameter/LArgument) and expect the original Value. JS_ASSERT(startFrameFormals.empty()); @@ -1343,16 +1344,18 @@ jit::BailoutIonToBaseline(JSContext *cx, RootedFunction fun(cx, callee); RootedScript scr(cx, iter.script()); AutoValueVector startFrameFormals(cx); RootedScript topCaller(cx); jsbytecode *topCallerPC = nullptr; while (true) { + MOZ_ASSERT(snapIter.instruction()->isResumePoint()); + #if JS_TRACE_LOGGING if (frameNo > 0) { TraceLogging::defaultLogger()->log(TraceLogging::SCRIPT_START, scr); TraceLogging::defaultLogger()->log(TraceLogging::INFO_ENGINE_BASELINE); } #endif IonSpew(IonSpew_BaselineBailouts, " FrameNo %d", frameNo); @@ -1378,26 +1381,27 @@ jit::BailoutIonToBaseline(JSContext *cx, break; JS_ASSERT(nextCallee); JS_ASSERT(callPC); caller = scr; callerPC = callPC; fun = nextCallee; scr = fun->existingScript(); - snapIter.nextFrame(); // Save top caller info for adjusting SPS frames later. if (!topCaller) { JS_ASSERT(frameNo == 0); topCaller = caller; topCallerPC = callerPC; } frameNo++; + + snapIter.nextInstruction(); } IonSpew(IonSpew_BaselineBailouts, " Done restoring frames"); // If there were multiple inline frames unpacked, and inline frame profiling // is off, then the current top SPS frame is for the outermost caller, and // has an uninitialized PC. Initialize it now. if (frameNo > 0 && !js_JitOptions.profileInlineFrames) cx->runtime()->spsProfiler.updatePC(topCaller, topCallerPC);
--- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -7933,25 +7933,25 @@ CodeGenerator::visitSetDOMProperty(LSetD return true; } typedef bool(*SPSFn)(JSContext *, HandleScript); static const VMFunction SPSEnterInfo = FunctionInfo<SPSFn>(SPSEnter); static const VMFunction SPSExitInfo = FunctionInfo<SPSFn>(SPSExit); bool -CodeGenerator::visitFunctionBoundary(LFunctionBoundary *lir) +CodeGenerator::visitProfilerStackOp(LProfilerStackOp *lir) { Register temp = ToRegister(lir->temp()->output()); bool inlinedFunction = lir->inlineLevel() > 0; switch (lir->type()) { - case MFunctionBoundary::Inline_Enter: + case MProfilerStackOp::InlineEnter: // Multiple scripts can be inlined at one depth, but there is only - // one Inline_Exit node to signify this. To deal with this, if we + // one InlineExit node to signify this. To deal with this, if we // reach the entry of another inline script on the same level, then // just reset the sps metadata about the frame. We must balance // calls to leave()/reenter(), so perform the balance without // emitting any instrumentation. Technically the previous inline // call at this same depth has reentered, but the instrumentation // will be emitted at the common join point for all inlines at the // same depth. if (sps_.inliningDepth() == lir->inlineLevel()) { @@ -7960,40 +7960,40 @@ CodeGenerator::visitFunctionBoundary(LFu sps_.reenter(masm, temp); } sps_.leave(masm, temp, /* inlinedFunction = */ true); if (!sps_.enterInlineFrame()) return false; // fallthrough - case MFunctionBoundary::Enter: + case MProfilerStackOp::Enter: if (gen->options.spsSlowAssertionsEnabled()) { if (!inlinedFunction || js_JitOptions.profileInlineFrames) { saveLive(lir); pushArg(ImmGCPtr(lir->script())); if (!callVM(SPSEnterInfo, lir)) return false; restoreLive(lir); } sps_.pushManual(lir->script(), masm, temp, /* inlinedFunction = */ inlinedFunction); return true; } return sps_.push(lir->script(), masm, temp, /* inlinedFunction = */ inlinedFunction); - case MFunctionBoundary::Inline_Exit: + case MProfilerStackOp::InlineExit: // all inline returns were covered with ::Exit, so we just need to // maintain the state of inline frames currently active and then // reenter the caller sps_.leaveInlineFrame(); sps_.reenter(masm, temp, /* inlinedFunction = */ true); return true; - case MFunctionBoundary::Exit: + case MProfilerStackOp::Exit: if (gen->options.spsSlowAssertionsEnabled()) { if (!inlinedFunction || js_JitOptions.profileInlineFrames) { saveLive(lir); pushArg(ImmGCPtr(lir->script())); // Once we've exited, then we shouldn't emit instrumentation for // the corresponding reenter() because we no longer have a // frame. sps_.skipNextReenter(); @@ -8003,17 +8003,17 @@ CodeGenerator::visitFunctionBoundary(LFu } return true; } sps_.pop(masm, temp, /* inlinedFunction = */ inlinedFunction); return true; default: - MOZ_ASSUME_UNREACHABLE("invalid LFunctionBoundary type"); + MOZ_ASSUME_UNREACHABLE("invalid LProfilerStackOp type"); } } bool CodeGenerator::visitOutOfLineAbortPar(OutOfLineAbortPar *ool) { ParallelBailoutCause cause = ool->cause(); jsbytecode *bytecode = ool->bytecode();
--- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -269,17 +269,17 @@ class CodeGenerator : public CodeGenerat bool visitBitNotV(LBitNotV *lir); bool visitBitOpV(LBitOpV *lir); bool emitInstanceOf(LInstruction *ins, JSObject *prototypeObject); bool visitIn(LIn *ins); bool visitInArray(LInArray *ins); bool visitInstanceOfO(LInstanceOfO *ins); bool visitInstanceOfV(LInstanceOfV *ins); bool visitCallInstanceOf(LCallInstanceOf *ins); - bool visitFunctionBoundary(LFunctionBoundary *lir); + bool visitProfilerStackOp(LProfilerStackOp *lir); bool visitGetDOMProperty(LGetDOMProperty *lir); bool visitGetDOMMember(LGetDOMMember *lir); bool visitSetDOMProperty(LSetDOMProperty *lir); bool visitCallDOMNative(LCallDOMNative *lir); bool visitCallGetIntrinsicValue(LCallGetIntrinsicValue *lir); bool visitIsCallable(LIsCallable *lir); bool visitHaveSameClass(LHaveSameClass *lir); bool visitHasClass(LHasClass *lir);
--- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -649,17 +649,17 @@ IonBuilder::build() MInstruction *argsObj = MConstant::New(alloc(), UndefinedValue()); current->add(argsObj); current->initSlot(info().argsObjSlot(), argsObj); } // Emit the start instruction, so we can begin real instructions. current->makeStart(MStart::New(alloc(), MStart::StartType_Default)); if (instrumentedProfiling()) - current->add(MFunctionBoundary::New(alloc(), script(), MFunctionBoundary::Enter)); + current->add(MProfilerStackOp::New(alloc(), script(), MProfilerStackOp::Enter)); // Guard against over-recursion. Do this before we start unboxing, since // this will create an OSI point that will read the incoming argument // values, which is nice to do before their last real use, to minimize // register/stack pressure. MCheckOverRecursed *check = MCheckOverRecursed::New(alloc()); current->add(check); check->setResumePoint(current->entryResumePoint()); @@ -786,21 +786,21 @@ IonBuilder::buildInline(IonBuilder *call // Connect the entrance block to the last block in the caller's graph. MBasicBlock *predecessor = callerBuilder->current; JS_ASSERT(predecessor == callerResumePoint->block()); // All further instructions generated in from this scope should be // considered as part of the function that we're inlining. We also need to // keep track of the inlining depth because all scripts inlined on the same - // level contiguously have only one Inline_Exit node. + // level contiguously have only one InlineExit node. if (instrumentedProfiling()) { - predecessor->add(MFunctionBoundary::New(alloc(), script(), - MFunctionBoundary::Inline_Enter, - inliningDepth_)); + predecessor->add(MProfilerStackOp::New(alloc(), script(), + MProfilerStackOp::InlineEnter, + inliningDepth_)); } predecessor->end(MGoto::New(alloc(), current)); if (!current->addPredecessorWithoutPhis(predecessor)) return false; // Initialize scope chain slot to Undefined. It's set later by |initScopeChain|. MInstruction *scope = MConstant::New(alloc(), UndefinedValue()); @@ -3623,18 +3623,18 @@ IonBuilder::processReturn(JSOp op) break; default: def = nullptr; MOZ_ASSUME_UNREACHABLE("unknown return op"); } if (instrumentedProfiling()) { - current->add(MFunctionBoundary::New(alloc(), script(), MFunctionBoundary::Exit, - inliningDepth_)); + current->add(MProfilerStackOp::New(alloc(), script(), MProfilerStackOp::Exit, + inliningDepth_)); } MReturn *ret = MReturn::New(alloc(), def); current->end(ret); if (!graph().addReturn(current)) return ControlStatus_Error; // Make sure no one tries to use this block now. @@ -3956,19 +3956,19 @@ IonBuilder::inlineScriptedCall(CallInfo // Create return block. jsbytecode *postCall = GetNextPc(pc); MBasicBlock *returnBlock = newBlock(nullptr, postCall); if (!returnBlock) return false; returnBlock->setCallerResumePoint(callerResumePoint_); - // When profiling add Inline_Exit instruction to indicate end of inlined function. + // When profiling add InlineExit instruction to indicate end of inlined function. if (instrumentedProfiling()) - returnBlock->add(MFunctionBoundary::New(alloc(), nullptr, MFunctionBoundary::Inline_Exit)); + returnBlock->add(MProfilerStackOp::New(alloc(), nullptr, MProfilerStackOp::InlineExit)); // Inherit the slots from current and pop |fun|. returnBlock->inheritSlots(current); returnBlock->pop(); // Accumulate return values. if (returns.empty()) { // Inlining of functions that have no exit is not supported.
--- a/js/src/jit/IonFrameIterator-inl.h +++ b/js/src/jit/IonFrameIterator-inl.h @@ -18,16 +18,17 @@ namespace js { namespace jit { template <AllowGC allowGC> inline InlineFrameIteratorMaybeGC<allowGC>::InlineFrameIteratorMaybeGC( JSContext *cx, const IonBailoutIterator *iter) : frame_(iter), framesRead_(0), + frameCount_(UINT32_MAX), callee_(cx), script_(cx) { if (iter) { start_ = SnapshotIterator(*iter); findNextFrame(); } }
--- a/js/src/jit/IonFrameIterator.h +++ b/js/src/jit/IonFrameIterator.h @@ -239,16 +239,18 @@ class IonFrameIterator void dump() const; inline BaselineFrame *baselineFrame() const; }; class IonJSFrameLayout; class IonBailoutIterator; +class RResumePoint; + // Reads frame information in snapshot-encoding order (that is, outermost frame // to innermost frame). class SnapshotIterator { SnapshotReader snapshot_; RecoverReader recover_; IonJSFrameLayout *fp_; MachineState machine_; @@ -282,55 +284,70 @@ class SnapshotIterator public: // Handle iterating over RValueAllocations of the snapshots. inline RValueAllocation readAllocation() { MOZ_ASSERT(moreAllocations()); return snapshot_.readAllocation(); } Value skip() { - readAllocation(); + snapshot_.skipAllocation(); return UndefinedValue(); } - inline uint32_t allocations() const { - return recover_.allocations(); + const RResumePoint *resumePoint() const; + const RInstruction *instruction() const { + return recover_.instruction(); } + + uint32_t numAllocations() const; inline bool moreAllocations() const { - return recover_.moreAllocations(snapshot_); + return snapshot_.numAllocationsRead() < numAllocations(); } public: // Exhibits frame properties contained in the snapshot. - inline uint32_t pcOffset() const { - return recover_.pcOffset(); - } + uint32_t pcOffset() const; inline bool resumeAfter() const { // Inline frames are inlined on calls, which are considered as being // resumed on the Call as baseline will push the pc once we return from // the call. if (moreFrames()) return false; return recover_.resumeAfter(); } inline BailoutKind bailoutKind() const { return snapshot_.bailoutKind(); } public: - // Handle iterating over frames of the snapshots. - inline void nextFrame() { - // Reuse the Snapshot buffer. - recover_.nextFrame(snapshot_); + // Read the next instruction available and get ready to either skip it or + // evaluate it. + inline void nextInstruction() { + MOZ_ASSERT(snapshot_.numAllocationsRead() == numAllocations()); + recover_.nextInstruction(); + snapshot_.resetNumAllocationsRead(); } - inline bool moreFrames() const { - return recover_.moreFrames(); + + // Skip an Instruction by walking to the next instruction and by skipping + // all the allocations corresponding to this instruction. + void skipInstruction(); + + inline bool moreInstructions() const { + return recover_.moreInstructions(); } - inline uint32_t frameCount() const { - return recover_.frameCount(); + + public: + // Handle iterating over frames of the snapshots. + void nextFrame(); + + inline bool moreFrames() const { + // The last instruction is recovering the innermost frame, so as long as + // there is more instruction there is necesseray more frames. + return moreInstructions(); } public: // Connect all informations about the current script in order to recover the // content of baseline frames. SnapshotIterator(IonScript *ionScript, SnapshotOffset snapshotOffset, IonJSFrameLayout *fp, const MachineState &machine); @@ -404,17 +421,24 @@ class SnapshotIterator // Reads frame information in callstack order (that is, innermost frame to // outermost frame). template <AllowGC allowGC=CanGC> class InlineFrameIteratorMaybeGC { const IonFrameIterator *frame_; SnapshotIterator start_; SnapshotIterator si_; - unsigned framesRead_; + uint32_t framesRead_; + + // When the inline-frame-iterator is created, this variable is defined to + // UINT32_MAX. Then the first iteration of findNextFrame, which settle on + // the innermost frame, is used to update this counter to the number of + // frames contained in the recover buffer. + uint32_t frameCount_; + typename MaybeRooted<JSFunction*, allowGC>::RootType callee_; typename MaybeRooted<JSScript*, allowGC>::RootType script_; jsbytecode *pc_; uint32_t numActualArgs_; struct Nop { void operator()(const Value &v) { } }; @@ -437,30 +461,31 @@ class InlineFrameIteratorMaybeGC resetOn(iter); } InlineFrameIteratorMaybeGC(JSContext *cx, const IonBailoutIterator *iter); InlineFrameIteratorMaybeGC(JSContext *cx, const InlineFrameIteratorMaybeGC *iter) : frame_(iter ? iter->frame_ : nullptr), framesRead_(0), + frameCount_(iter ? iter->frameCount_ : UINT32_MAX), callee_(cx), script_(cx) { if (frame_) { start_ = SnapshotIterator(*frame_); // findNextFrame will iterate to the next frame and init. everything. // Therefore to settle on the same frame, we report one frame less readed. framesRead_ = iter->framesRead_ - 1; findNextFrame(); } } bool more() const { - return frame_ && framesRead_ < start_.frameCount(); + return frame_ && framesRead_ < frameCount_; } JSFunction *callee() const { JS_ASSERT(callee_); return callee_; } JSFunction *maybeCallee() const { return callee_; } @@ -505,18 +530,18 @@ class InlineFrameIteratorMaybeGC InlineFrameIteratorMaybeGC it(cx, this); ++it; unsigned argsObjAdj = it.script()->argumentsHasVarBinding() ? 1 : 0; SnapshotIterator parent_s(it.snapshotIterator()); // Skip over all slots until we get to the last slots // (= arguments slots of callee) the +3 is for [this], [returnvalue], // [scopechain], and maybe +1 for [argsObj] - JS_ASSERT(parent_s.allocations() >= nactual + 3 + argsObjAdj); - unsigned skip = parent_s.allocations() - nactual - 3 - argsObjAdj; + JS_ASSERT(parent_s.numAllocations() >= nactual + 3 + argsObjAdj); + unsigned skip = parent_s.numAllocations() - nactual - 3 - argsObjAdj; for (unsigned j = 0; j < skip; j++) parent_s.skip(); // Get the overflown arguments parent_s.readFrameArgs(argOp, nullptr, nullptr, nformal, nactual, it.script()); } else { // There is no parent frame to this inlined frame, we can read // from the frame's Value vector directly. @@ -592,17 +617,18 @@ class InlineFrameIteratorMaybeGC void resetOn(const IonFrameIterator *iter); const IonFrameIterator &frame() const { return *frame_; } // Inline frame number, 0 for the outermost (non-inlined) frame. size_t frameNo() const { - return start_.frameCount() - framesRead_; + MOZ_ASSERT(frameCount_ != UINT32_MAX); + return frameCount_ - framesRead_; } private: InlineFrameIteratorMaybeGC() MOZ_DELETE; InlineFrameIteratorMaybeGC(const InlineFrameIteratorMaybeGC &iter) MOZ_DELETE; }; typedef InlineFrameIteratorMaybeGC<CanGC> InlineFrameIterator; typedef InlineFrameIteratorMaybeGC<NoGC> InlineFrameIteratorNoGC;
--- a/js/src/jit/IonFrames.cpp +++ b/js/src/jit/IonFrames.cpp @@ -15,16 +15,17 @@ #include "jit/BaselineIC.h" #include "jit/BaselineJIT.h" #include "jit/Ion.h" #include "jit/IonMacroAssembler.h" #include "jit/IonSpewer.h" #include "jit/JitCompartment.h" #include "jit/ParallelFunctions.h" #include "jit/PcScriptCache.h" +#include "jit/Recover.h" #include "jit/Safepoints.h" #include "jit/Snapshots.h" #include "jit/VMFunctions.h" #include "vm/ForkJoin.h" #include "vm/Interpreter.h" #include "jit/IonFrameIterator-inl.h" #include "vm/Probes-inl.h" @@ -1493,16 +1494,52 @@ SnapshotIterator::allocationValue(const } #endif default: MOZ_ASSUME_UNREACHABLE("huh?"); } } +const RResumePoint * +SnapshotIterator::resumePoint() const +{ + return instruction()->toResumePoint(); +} + +uint32_t +SnapshotIterator::numAllocations() const +{ + return resumePoint()->numOperands(); +} + +uint32_t +SnapshotIterator::pcOffset() const +{ + return resumePoint()->pcOffset(); +} + +void +SnapshotIterator::skipInstruction() +{ + MOZ_ASSERT(snapshot_.numAllocationsRead() == 0); + size_t numOperands = instruction()->numOperands(); + for (size_t i = 0; i < numOperands; i++) + skip(); + nextInstruction(); +} + +void +SnapshotIterator::nextFrame() +{ + nextInstruction(); + while (!instruction()->isResumePoint()) + skipInstruction(); +} + IonScript * IonFrameIterator::ionScript() const { JS_ASSERT(type() == JitFrame_IonJS); IonScript *ionScript = nullptr; if (checkInvalidation(&ionScript)) return ionScript; @@ -1531,16 +1568,17 @@ IonFrameIterator::osiIndex() const } template <AllowGC allowGC> void InlineFrameIteratorMaybeGC<allowGC>::resetOn(const IonFrameIterator *iter) { frame_ = iter; framesRead_ = 0; + frameCount_ = UINT32_MAX; if (iter) { start_ = SnapshotIterator(*iter); findNextFrame(); } } template void InlineFrameIteratorMaybeGC<NoGC>::resetOn(const IonFrameIterator *iter); template void InlineFrameIteratorMaybeGC<CanGC>::resetOn(const IonFrameIterator *iter); @@ -1548,28 +1586,41 @@ template void InlineFrameIteratorMaybeGC template <AllowGC allowGC> void InlineFrameIteratorMaybeGC<allowGC>::findNextFrame() { JS_ASSERT(more()); si_ = start_; - // Read the initial frame. + // Read the initial frame out of the C stack. callee_ = frame_->maybeCallee(); script_ = frame_->script(); + + // Settle on the outermost frame without evaluating any instructions before + // looking for a pc. + if (!si_.instruction()->isResumePoint()) + si_.nextFrame(); + pc_ = script_->offsetToPC(si_.pcOffset()); #ifdef DEBUG numActualArgs_ = 0xbadbad; #endif // This unfortunately is O(n*m), because we must skip over outer frames // before reading inner ones. - unsigned remaining = start_.frameCount() - framesRead_ - 1; - for (unsigned i = 0; i < remaining; i++) { + + // The first time (frameCount_ == UINT32_MAX) we do not know the number of + // frames that we are going to inspect. So we are iterating until there is + // no more frames, to settle on the inner most frame and to count the number + // of frames. + size_t remaining = (frameCount_ != UINT32_MAX) ? frameNo() - 1 : SIZE_MAX; + + size_t i = 1; + for (; i <= remaining && si_.moreFrames(); i++) { JS_ASSERT(IsIonInlinablePC(pc_)); // Recover the number of actual arguments from the script. if (JSOp(*pc_) != JSOP_FUNAPPLY) numActualArgs_ = GET_ARGC(pc_); if (JSOp(*pc_) == JSOP_FUNCALL) { JS_ASSERT(GET_ARGC(pc_) > 0); numActualArgs_ = GET_ARGC(pc_) - 1; @@ -1577,20 +1628,21 @@ InlineFrameIteratorMaybeGC<allowGC>::fin numActualArgs_ = 0; } else if (IsSetPropPC(pc_)) { numActualArgs_ = 1; } JS_ASSERT(numActualArgs_ != 0xbadbad); // Skip over non-argument slots, as well as |this|. - unsigned skipCount = (si_.allocations() - 1) - numActualArgs_ - 1; + unsigned skipCount = (si_.numAllocations() - 1) - numActualArgs_ - 1; for (unsigned j = 0; j < skipCount; j++) si_.skip(); + // The JSFunction is a constant, otherwise we would not have inlined it. Value funval = si_.read(); // Skip extra value allocations. while (si_.moreAllocations()) si_.skip(); si_.nextFrame(); @@ -1599,16 +1651,24 @@ InlineFrameIteratorMaybeGC<allowGC>::fin // Inlined functions may be clones that still point to the lazy script // for the executed script, if they are clones. The actual script // exists though, just make sure the function points to it. script_ = callee_->existingScript(); pc_ = script_->offsetToPC(si_.pcOffset()); } + // The first time we do not know the number of frames, we only settle on the + // last frame, and update the number of frames based on the number of + // iteration that we have done. + if (frameCount_ == UINT32_MAX) { + MOZ_ASSERT(!si_.moreFrames()); + frameCount_ = i; + } + framesRead_++; } template void InlineFrameIteratorMaybeGC<NoGC>::findNextFrame(); template void InlineFrameIteratorMaybeGC<CanGC>::findNextFrame(); template <AllowGC allowGC> bool InlineFrameIteratorMaybeGC<allowGC>::isFunctionFrame() const @@ -1799,18 +1859,18 @@ InlineFrameIteratorMaybeGC<allowGC>::dum fprintf(stderr, " script = %p, pc = %p\n", (void*) script(), pc()); fprintf(stderr, " current op: %s\n", js_CodeName[*pc()]); if (!more()) { numActualArgs(); } SnapshotIterator si = snapshotIterator(); - fprintf(stderr, " slots: %u\n", si.allocations() - 1); - for (unsigned i = 0; i < si.allocations() - 1; i++) { + fprintf(stderr, " slots: %u\n", si.numAllocations() - 1); + for (unsigned i = 0; i < si.numAllocations() - 1; i++) { if (isFunction) { if (i == 0) fprintf(stderr, " scope chain: "); else if (i == 1) fprintf(stderr, " this: "); else if (i - 2 < callee()->nargs()) fprintf(stderr, " formal (arg %d): ", i - 2); else {
--- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -5690,39 +5690,39 @@ class LCallInstanceOf : public LCallInst const LAllocation *rhs() { return getOperand(RHS); } static const size_t LHS = 0; static const size_t RHS = BOX_PIECES; }; -class LFunctionBoundary : public LInstructionHelper<0, 0, 1> -{ - public: - LIR_HEADER(FunctionBoundary) - - LFunctionBoundary(const LDefinition &temp) { +class LProfilerStackOp : public LInstructionHelper<0, 0, 1> +{ + public: + LIR_HEADER(ProfilerStackOp) + + LProfilerStackOp(const LDefinition &temp) { setTemp(0, temp); } const LDefinition *temp() { return getTemp(0); } JSScript *script() { - return mir_->toFunctionBoundary()->script(); - } - - MFunctionBoundary::Type type() { - return mir_->toFunctionBoundary()->type(); + return mir_->toProfilerStackOp()->script(); + } + + MProfilerStackOp::Type type() { + return mir_->toProfilerStackOp()->type(); } unsigned inlineLevel() { - return mir_->toFunctionBoundary()->inlineLevel(); + return mir_->toProfilerStackOp()->inlineLevel(); } }; class LIsCallable : public LInstructionHelper<1, 1, 0> { public: LIR_HEADER(IsCallable); LIsCallable(const LAllocation &object) {
--- a/js/src/jit/LIR.cpp +++ b/js/src/jit/LIR.cpp @@ -114,38 +114,58 @@ static size_t TotalOperandCount(MResumePoint *mir) { size_t accum = mir->numOperands(); while ((mir = mir->caller())) accum += mir->numOperands(); return accum; } -LRecoverInfo::LRecoverInfo(MResumePoint *mir) - : mir_(mir), +LRecoverInfo::LRecoverInfo(TempAllocator &alloc) + : instructions_(alloc), recoverOffset_(INVALID_RECOVER_OFFSET) { } LRecoverInfo * LRecoverInfo::New(MIRGenerator *gen, MResumePoint *mir) { - LRecoverInfo *recover = new(gen->alloc()) LRecoverInfo(mir); - if (!recover) + LRecoverInfo *recoverInfo = new(gen->alloc()) LRecoverInfo(gen->alloc()); + if (!recoverInfo || !recoverInfo->init(mir)) return nullptr; - IonSpew(IonSpew_Snapshots, "Generating LIR recover %p from MIR (%p)", - (void *)recover, (void *)mir); + IonSpew(IonSpew_Snapshots, "Generating LIR recover info %p from MIR (%p)", + (void *)recoverInfo, (void *)mir); - return recover; + return recoverInfo; } -LSnapshot::LSnapshot(LRecoverInfo *recover, BailoutKind kind) - : numSlots_(TotalOperandCount(recover->mir()) * BOX_PIECES), +bool +LRecoverInfo::init(MResumePoint *rp) +{ + MResumePoint *it = rp; + + // Sort operations in the order in which we need to restore the stack. This + // implies that outer frames, as well as operations needed to recover the + // current frame, are located before the current frame. The inner-most + // resume point should be the last element in the list. + do { + if (!instructions_.append(it)) + return false; + it = it->caller(); + } while (it); + + Reverse(instructions_.begin(), instructions_.end()); + MOZ_ASSERT(mir() == rp); + return true; +} + +LSnapshot::LSnapshot(LRecoverInfo *recoverInfo, BailoutKind kind) + : numSlots_(TotalOperandCount(recoverInfo->mir()) * BOX_PIECES), slots_(nullptr), - recoverInfo_(recover), + recoverInfo_(recoverInfo), snapshotOffset_(INVALID_SNAPSHOT_OFFSET), bailoutId_(INVALID_BAILOUT_ID), bailoutKind_(kind) { } bool LSnapshot::init(MIRGenerator *gen) {
--- a/js/src/jit/LIR.h +++ b/js/src/jit/LIR.h @@ -873,36 +873,51 @@ class LCallInstructionHelper : public LI public: virtual bool isCall() const { return true; } }; class LRecoverInfo : public TempObject { - MResumePoint *mir_; + public: + typedef Vector<MResumePoint *, 2, IonAllocPolicy> Instructions; + + private: + // List of instructions needed to recover the stack frames. + // Outer frames are stored before inner frames. + Instructions instructions_; // Cached offset where this resume point is encoded. RecoverOffset recoverOffset_; - LRecoverInfo(MResumePoint *mir); + LRecoverInfo(TempAllocator &alloc); + bool init(MResumePoint *mir); public: static LRecoverInfo *New(MIRGenerator *gen, MResumePoint *mir); + // Resume point of the inner most function. MResumePoint *mir() const { - return mir_; + return instructions_.back(); } RecoverOffset recoverOffset() const { return recoverOffset_; } void setRecoverOffset(RecoverOffset offset) { JS_ASSERT(recoverOffset_ == INVALID_RECOVER_OFFSET); recoverOffset_ = offset; } + + MResumePoint **begin() { + return instructions_.begin(); + } + MResumePoint **end() { + return instructions_.end(); + } }; // An LSnapshot is the reflection of an MResumePoint in LIR. Unlike MResumePoints, // they cannot be shared, as they are filled in by the register allocator in // order to capture the precise low-level stack state in between an // instruction's input and output. During code generation, LSnapshots are // compressed and saved in the compiled script. class LSnapshot : public TempObject
--- a/js/src/jit/LOpcodes.h +++ b/js/src/jit/LOpcodes.h @@ -267,17 +267,17 @@ _(RoundF) \ _(In) \ _(InArray) \ _(InstanceOfO) \ _(InstanceOfV) \ _(CallInstanceOf) \ _(InterruptCheck) \ _(InterruptCheckImplicit) \ - _(FunctionBoundary) \ + _(ProfilerStackOp) \ _(GetDOMProperty) \ _(GetDOMMember) \ _(SetDOMProperty) \ _(CallDOMNative) \ _(IsCallable) \ _(HaveSameClass) \ _(HasClass) \ _(AsmJSLoadHeap) \
--- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -3353,19 +3353,19 @@ LIRGenerator::visitCallInstanceOf(MCallI LCallInstanceOf *lir = new(alloc()) LCallInstanceOf(useRegisterAtStart(rhs)); if (!useBoxAtStart(lir, LCallInstanceOf::LHS, lhs)) return false; return defineReturn(lir, ins) && assignSafepoint(lir, ins); } bool -LIRGenerator::visitFunctionBoundary(MFunctionBoundary *ins) +LIRGenerator::visitProfilerStackOp(MProfilerStackOp *ins) { - LFunctionBoundary *lir = new(alloc()) LFunctionBoundary(temp()); + LProfilerStackOp *lir = new(alloc()) LProfilerStackOp(temp()); if (!add(lir, ins)) return false; // If slow assertions are enabled, then this node will result in a callVM // out to a C++ function for the assertions, so we will need a safepoint. return !gen->options.spsSlowAssertionsEnabled() || assignSafepoint(lir, ins); } bool @@ -3527,18 +3527,21 @@ LIRGenerator::visitGetDOMProperty(MGetDO return defineReturn(lir, ins) && assignSafepoint(lir, ins); } bool LIRGenerator::visitGetDOMMember(MGetDOMMember *ins) { MOZ_ASSERT(ins->isDomMovable(), "Members had better be movable"); - MOZ_ASSERT(ins->domAliasSet() == JSJitInfo::AliasNone, - "Members had better not alias anything"); + // We wish we could assert that ins->domAliasSet() == JSJitInfo::AliasNone, + // but some MGetDOMMembers are for [Pure], not [Constant] properties, whose + // value can in fact change as a result of DOM setters and method calls. + MOZ_ASSERT(ins->domAliasSet() != JSJitInfo::AliasEverything, + "Member gets had better not alias the world"); LGetDOMMember *lir = new(alloc()) LGetDOMMember(useRegister(ins->object())); return defineBox(lir, ins); } bool LIRGenerator::visitRecompileCheck(MRecompileCheck *ins) {
--- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -233,17 +233,17 @@ class LIRGenerator : public LIRGenerator bool visitRunOncePrologue(MRunOncePrologue *ins); bool visitRest(MRest *ins); bool visitRestPar(MRestPar *ins); bool visitThrow(MThrow *ins); bool visitIn(MIn *ins); bool visitInArray(MInArray *ins); bool visitInstanceOf(MInstanceOf *ins); bool visitCallInstanceOf(MCallInstanceOf *ins); - bool visitFunctionBoundary(MFunctionBoundary *ins); + bool visitProfilerStackOp(MProfilerStackOp *ins); bool visitIsCallable(MIsCallable *ins); bool visitHaveSameClass(MHaveSameClass *ins); bool visitHasClass(MHasClass *ins); bool visitAsmJSLoadGlobalVar(MAsmJSLoadGlobalVar *ins); bool visitAsmJSStoreGlobalVar(MAsmJSStoreGlobalVar *ins); bool visitAsmJSLoadFFIFunc(MAsmJSLoadFFIFunc *ins); bool visitAsmJSParameter(MAsmJSParameter *ins); bool visitAsmJSReturn(MAsmJSReturn *ins);
--- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -9212,48 +9212,48 @@ class MNewStringObject : TypePolicy *typePolicy() { return this; } }; // Node that represents that a script has begun executing. This comes at the // start of the function and is called once per function (including inline // ones) -class MFunctionBoundary : public MNullaryInstruction +class MProfilerStackOp : public MNullaryInstruction { public: enum Type { Enter, // a function has begun executing and it is not inline Exit, // any function has exited (inlined or normal) - Inline_Enter, // an inline function has begun executing - - Inline_Exit // all instructions of an inline function are done, a + InlineEnter, // an inline function has begun executing + + InlineExit // all instructions of an inline function are done, a // return from the inline function could have occurred // before this boundary }; private: JSScript *script_; Type type_; unsigned inlineLevel_; - MFunctionBoundary(JSScript *script, Type type, unsigned inlineLevel) + MProfilerStackOp(JSScript *script, Type type, unsigned inlineLevel) : script_(script), type_(type), inlineLevel_(inlineLevel) { - JS_ASSERT_IF(type != Inline_Exit, script != nullptr); - JS_ASSERT_IF(type == Inline_Enter, inlineLevel != 0); + JS_ASSERT_IF(type != InlineExit, script != nullptr); + JS_ASSERT_IF(type == InlineEnter, inlineLevel != 0); setGuard(); } public: - INSTRUCTION_HEADER(FunctionBoundary) - - static MFunctionBoundary *New(TempAllocator &alloc, JSScript *script, Type type, + INSTRUCTION_HEADER(ProfilerStackOp) + + static MProfilerStackOp *New(TempAllocator &alloc, JSScript *script, Type type, unsigned inlineLevel = 0) { - return new(alloc) MFunctionBoundary(script, type, inlineLevel); + return new(alloc) MProfilerStackOp(script, type, inlineLevel); } JSScript *script() { return script_; } Type type() { return type_; @@ -9420,54 +9420,18 @@ class MResumePoint MOZ_FINAL : public MN } void discardUses() { for (size_t i = 0; i < stackDepth_; i++) { if (operands_[i].hasProducer()) operands_[i].producer()->removeUse(&operands_[i]); } } -}; - -/* - * Facade for a chain of MResumePoints that cross frame boundaries (due to - * function inlining). Operands are ordered from oldest frame to newest. - */ -class FlattenedMResumePointIter -{ - Vector<MResumePoint *, 8, SystemAllocPolicy> resumePoints; - MResumePoint *newest; - size_t numOperands_; - - public: - explicit FlattenedMResumePointIter(MResumePoint *newest) - : newest(newest), numOperands_(0) - {} - - bool init() { - MResumePoint *it = newest; - do { - if (!resumePoints.append(it)) - return false; - it = it->caller(); - } while (it); - Reverse(resumePoints.begin(), resumePoints.end()); - return true; - } - - MResumePoint **begin() { - return resumePoints.begin(); - } - MResumePoint **end() { - return resumePoints.end(); - } - - size_t numOperands() const { - return numOperands_; - } + + bool writeRecoverData(CompactBufferWriter &writer) const; }; class MIsCallable : public MUnaryInstruction, public SingleObjectPolicy { MIsCallable(MDefinition *object) : MUnaryInstruction(object)
--- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -182,17 +182,17 @@ namespace jit { _(RunOncePrologue) \ _(Rest) \ _(Floor) \ _(Round) \ _(In) \ _(InstanceOf) \ _(CallInstanceOf) \ _(InterruptCheck) \ - _(FunctionBoundary) \ + _(ProfilerStackOp) \ _(GetDOMProperty) \ _(GetDOMMember) \ _(SetDOMProperty) \ _(IsCallable) \ _(HaveSameClass) \ _(HasClass) \ _(AsmJSNeg) \ _(AsmJSUnsignedToDouble) \
--- a/js/src/jit/ParallelSafetyAnalysis.cpp +++ b/js/src/jit/ParallelSafetyAnalysis.cpp @@ -286,17 +286,17 @@ class ParallelSafetyVisitor : public MIn UNSAFE_OP(Random) SAFE_OP(Pow) SAFE_OP(PowHalf) UNSAFE_OP(RegExpTest) UNSAFE_OP(RegExpExec) UNSAFE_OP(RegExpReplace) UNSAFE_OP(StringReplace) UNSAFE_OP(CallInstanceOf) - UNSAFE_OP(FunctionBoundary) + UNSAFE_OP(ProfilerStackOp) UNSAFE_OP(GuardString) UNSAFE_OP(NewDeclEnvObject) UNSAFE_OP(In) UNSAFE_OP(InArray) SAFE_OP(GuardThreadExclusive) SAFE_OP(InterruptCheckPar) SAFE_OP(CheckOverRecursedPar) SAFE_OP(FunctionDispatch)
new file mode 100644 --- /dev/null +++ b/js/src/jit/Recover.cpp @@ -0,0 +1,110 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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 "jit/Recover.h" + +#include "jit/IonSpewer.h" +#include "jit/MIR.h" +#include "jit/MIRGraph.h" + +using namespace js; +using namespace js::jit; + +void +RInstruction::readRecoverData(CompactBufferReader &reader, RInstructionStorage *raw) +{ + uint32_t op = reader.readUnsigned(); + switch (Opcode(op)) { + case Recover_ResumePoint: + new (raw->addr()) RResumePoint(reader); + break; + default: + MOZ_ASSUME_UNREACHABLE("Bad decoding of the previous instruction?"); + break; + } +} + +bool +MResumePoint::writeRecoverData(CompactBufferWriter &writer) const +{ + writer.writeUnsigned(uint32_t(RInstruction::Recover_ResumePoint)); + + MBasicBlock *bb = block(); + JSFunction *fun = bb->info().funMaybeLazy(); + JSScript *script = bb->info().script(); + uint32_t exprStack = stackDepth() - bb->info().ninvoke(); + +#ifdef DEBUG + // Ensure that all snapshot which are encoded can safely be used for + // bailouts. + if (GetIonContext()->cx) { + uint32_t stackDepth; + bool reachablePC; + jsbytecode *bailPC = pc(); + + if (mode() == MResumePoint::ResumeAfter) + bailPC = GetNextPc(pc()); + + if (!ReconstructStackDepth(GetIonContext()->cx, script, + bailPC, &stackDepth, &reachablePC)) + { + return false; + } + + if (reachablePC) { + if (JSOp(*bailPC) == JSOP_FUNCALL) { + // For fun.call(this, ...); the reconstructStackDepth will + // include the this. When inlining that is not included. So the + // exprStackSlots will be one less. + MOZ_ASSERT(stackDepth - exprStack <= 1); + } else if (JSOp(*bailPC) != JSOP_FUNAPPLY && + !IsGetPropPC(bailPC) && !IsSetPropPC(bailPC)) + { + // For fun.apply({}, arguments) the reconstructStackDepth will + // have stackdepth 4, but it could be that we inlined the + // funapply. In that case exprStackSlots, will have the real + // arguments in the slots and not be 4. + + // With accessors, we have different stack depths depending on + // whether or not we inlined the accessor, as the inlined stack + // contains a callee function that should never have been there + // and we might just be capturing an uneventful property site, + // in which case there won't have been any violence. + MOZ_ASSERT(exprStack == stackDepth); + } + } + } +#endif + + // Test if we honor the maximum of arguments at all times. This is a sanity + // check and not an algorithm limit. So check might be a bit too loose. +4 + // to account for scope chain, return value, this value and maybe + // arguments_object. + MOZ_ASSERT(CountArgSlots(script, fun) < SNAPSHOT_MAX_NARGS + 4); + + uint32_t implicit = StartArgSlot(script); + uint32_t formalArgs = CountArgSlots(script, fun); + uint32_t nallocs = formalArgs + script->nfixed() + exprStack; + + IonSpew(IonSpew_Snapshots, "Starting frame; implicit %u, formals %u, fixed %u, exprs %u", + implicit, formalArgs - implicit, script->nfixed(), exprStack); + + uint32_t pcoff = script->pcToOffset(pc()); + IonSpew(IonSpew_Snapshots, "Writing pc offset %u, nslots %u", pcoff, nallocs); + writer.writeUnsigned(pcoff); + writer.writeUnsigned(nallocs); + return true; +} + +RResumePoint::RResumePoint(CompactBufferReader &reader) +{ + static_assert(sizeof(*this) <= sizeof(RInstructionStorage), + "Storage space is too small to decode this recover instruction."); + pcOffset_ = reader.readUnsigned(); + numOperands_ = reader.readUnsigned(); + IonSpew(IonSpew_Snapshots, "Read RResumePoint (pc offset %u, nslots %u)", + pcOffset_, numOperands_); +}
new file mode 100644 --- /dev/null +++ b/js/src/jit/Recover.h @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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 jit_Recover_h +#define jit_Recover_h + +#include "mozilla/Attributes.h" + +#include "jit/Snapshots.h" + +namespace js { +namespace jit { + +class RResumePoint; + +class RInstruction +{ + public: + enum Opcode + { + Recover_ResumePoint = 0 + }; + + virtual Opcode opcode() const = 0; + + bool isResumePoint() const { + return opcode() == Recover_ResumePoint; + } + inline const RResumePoint *toResumePoint() const; + + virtual uint32_t numOperands() const = 0; + + static void readRecoverData(CompactBufferReader &reader, RInstructionStorage *raw); +}; + +class RResumePoint MOZ_FINAL : public RInstruction +{ + private: + uint32_t pcOffset_; // Offset from script->code. + uint32_t numOperands_; // Number of slots. + + friend class RInstruction; + RResumePoint(CompactBufferReader &reader); + + public: + virtual Opcode opcode() const { + return Recover_ResumePoint; + } + + uint32_t pcOffset() const { + return pcOffset_; + } + virtual uint32_t numOperands() const { + return numOperands_; + } +}; + +const RResumePoint * +RInstruction::toResumePoint() const +{ + MOZ_ASSERT(isResumePoint()); + return static_cast<const RResumePoint *>(this); +} + +} +} + +#endif /* jit_Recover_h */
--- a/js/src/jit/Snapshots.cpp +++ b/js/src/jit/Snapshots.cpp @@ -7,18 +7,19 @@ #include "jit/Snapshots.h" #include "jsscript.h" #include "jit/CompileInfo.h" #include "jit/IonSpewer.h" #ifdef TRACK_SNAPSHOTS # include "jit/LIR.h" -# include "jit/MIR.h" #endif +#include "jit/MIR.h" +#include "jit/Recover.h" using namespace js; using namespace js::jit; // Snapshot header: // // [vwu] bits ((n+1)-31]: frame count // bit n+1: resume after @@ -476,19 +477,19 @@ static const uint32_t SNAPSHOT_ROFFSET_S static const uint32_t SNAPSHOT_ROFFSET_BITS = 32 - SNAPSHOT_ROFFSET_SHIFT; static const uint32_t SNAPSHOT_ROFFSET_MASK = COMPUTE_MASK_(SNAPSHOT_ROFFSET); // Details of recover header packing. static const uint32_t RECOVER_RESUMEAFTER_SHIFT = 0; static const uint32_t RECOVER_RESUMEAFTER_BITS = 1; static const uint32_t RECOVER_RESUMEAFTER_MASK = COMPUTE_MASK_(RECOVER_RESUMEAFTER); -static const uint32_t RECOVER_FRAMECOUNT_SHIFT = COMPUTE_SHIFT_AFTER_(RECOVER_RESUMEAFTER); -static const uint32_t RECOVER_FRAMECOUNT_BITS = 32 - RECOVER_FRAMECOUNT_SHIFT; -static const uint32_t RECOVER_FRAMECOUNT_MASK = COMPUTE_MASK_(RECOVER_FRAMECOUNT); +static const uint32_t RECOVER_RINSCOUNT_SHIFT = COMPUTE_SHIFT_AFTER_(RECOVER_RESUMEAFTER); +static const uint32_t RECOVER_RINSCOUNT_BITS = 32 - RECOVER_RINSCOUNT_SHIFT; +static const uint32_t RECOVER_RINSCOUNT_MASK = COMPUTE_MASK_(RECOVER_RINSCOUNT); #undef COMPUTE_MASK_ #undef COMPUTE_SHIFT_AFTER_ void SnapshotReader::readSnapshotHeader() { uint32_t bits = reader_.readUnsigned(); @@ -525,74 +526,72 @@ SnapshotReader::spewBailingFrom() const fprintf(IonSpewFile, " [%u], LIR: ", mirId_); LInstruction::printName(IonSpewFile, LInstruction::Opcode(lirOpcode_)); fprintf(IonSpewFile, " [%u]", lirId_); fprintf(IonSpewFile, "\n"); } } #endif +uint32_t +SnapshotReader::readAllocationIndex() +{ + allocRead_++; + return reader_.readUnsigned(); +} + RValueAllocation SnapshotReader::readAllocation() { IonSpew(IonSpew_Snapshots, "Reading slot %u", allocRead_); - allocRead_++; - - uint32_t offset = reader_.readUnsigned() * ALLOCATION_TABLE_ALIGNMENT; + uint32_t offset = readAllocationIndex() * ALLOCATION_TABLE_ALIGNMENT; allocReader_.seek(allocTable_, offset); return RValueAllocation::read(allocReader_); } bool SnapshotWriter::init() { // Based on the measurements made in Bug 962555 comment 20, this should be // enough to prevent the reallocation of the hash table for at least half of // the compilations. return allocMap_.init(32); } RecoverReader::RecoverReader(SnapshotReader &snapshot, const uint8_t *recovers, uint32_t size) : reader_(nullptr, nullptr), - frameCount_(0), - framesRead_(0), - allocCount_(0) + numInstructions_(0), + numInstructionsRead_(0) { if (!recovers) return; reader_ = CompactBufferReader(recovers + snapshot.recoverOffset(), recovers + size); readRecoverHeader(); - readFrame(snapshot); + readInstruction(); } void RecoverReader::readRecoverHeader() { uint32_t bits = reader_.readUnsigned(); - frameCount_ = (bits & RECOVER_FRAMECOUNT_MASK) >> RECOVER_FRAMECOUNT_SHIFT; + numInstructions_ = (bits & RECOVER_RINSCOUNT_MASK) >> RECOVER_RINSCOUNT_SHIFT; resumeAfter_ = (bits & RECOVER_RESUMEAFTER_MASK) >> RECOVER_RESUMEAFTER_SHIFT; - JS_ASSERT(frameCount_); + MOZ_ASSERT(numInstructions_); - IonSpew(IonSpew_Snapshots, "Read recover header with frameCount %u (ra: %d)", - frameCount_, resumeAfter_); + IonSpew(IonSpew_Snapshots, "Read recover header with instructionCount %u (ra: %d)", + numInstructions_, resumeAfter_); } void -RecoverReader::readFrame(SnapshotReader &snapshot) +RecoverReader::readInstruction() { - JS_ASSERT(moreFrames()); - JS_ASSERT(snapshot.allocRead_ == allocCount_); - - pcOffset_ = reader_.readUnsigned(); - allocCount_ = reader_.readUnsigned(); - IonSpew(IonSpew_Snapshots, "Read pc offset %u, nslots %u", pcOffset_, allocCount_); - - framesRead_++; - snapshot.allocRead_ = 0; + MOZ_ASSERT(moreInstructions()); + RInstruction::readRecoverData(reader_, &rawData_); + numInstructionsRead_++; } SnapshotOffset SnapshotWriter::startSnapshot(RecoverOffset recoverOffset, BailoutKind kind) { lastStart_ = writer_.length(); allocWritten_ = 0; @@ -668,46 +667,32 @@ RecoverWriter::startRecover(uint32_t fra MOZ_ASSERT(frameCount); nframes_ = frameCount; framesWritten_ = 0; IonSpew(IonSpew_Snapshots, "starting recover with frameCount %u", frameCount); MOZ_ASSERT(!(uint32_t(resumeAfter) &~ RECOVER_RESUMEAFTER_MASK)); - MOZ_ASSERT(frameCount < uint32_t(1 << RECOVER_FRAMECOUNT_BITS)); + MOZ_ASSERT(frameCount < uint32_t(1 << RECOVER_RINSCOUNT_BITS)); uint32_t bits = (uint32_t(resumeAfter) << RECOVER_RESUMEAFTER_SHIFT) | - (frameCount << RECOVER_FRAMECOUNT_SHIFT); + (frameCount << RECOVER_RINSCOUNT_SHIFT); RecoverOffset recoverOffset = writer_.length(); writer_.writeUnsigned(bits); return recoverOffset; } -void -RecoverWriter::writeFrame(JSFunction *fun, JSScript *script, - jsbytecode *pc, uint32_t exprStack) +bool +RecoverWriter::writeFrame(const MResumePoint *rp) { - // Test if we honor the maximum of arguments at all times. - // This is a sanity check and not an algorithm limit. So check might be a bit too loose. - // +4 to account for scope chain, return value, this value and maybe arguments_object. - JS_ASSERT(CountArgSlots(script, fun) < SNAPSHOT_MAX_NARGS + 4); - - uint32_t implicit = StartArgSlot(script); - uint32_t formalArgs = CountArgSlots(script, fun); - uint32_t nallocs = formalArgs + script->nfixed() + exprStack; - - IonSpew(IonSpew_Snapshots, "Starting frame; implicit %u, formals %u, fixed %u, exprs %u", - implicit, formalArgs - implicit, script->nfixed(), exprStack); - - uint32_t pcoff = script->pcToOffset(pc); - IonSpew(IonSpew_Snapshots, "Writing pc offset %u, nslots %u", pcoff, nallocs); - writer_.writeUnsigned(pcoff); - writer_.writeUnsigned(nallocs); + if (!rp->writeRecoverData(writer_)) + return false; framesWritten_++; + return true; } void RecoverWriter::endRecover() { JS_ASSERT(nframes_ == framesWritten_); }
--- a/js/src/jit/Snapshots.h +++ b/js/src/jit/Snapshots.h @@ -2,16 +2,18 @@ * vim: set ts=8 sts=4 et sw=4 tw=99: * 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 jit_Snapshot_h #define jit_Snapshot_h +#include "mozilla/Alignment.h" + #include "jsalloc.h" #include "jsbytecode.h" #include "jit/CompactBuffer.h" #include "jit/IonTypes.h" #include "jit/Registers.h" #include "js/HashTable.h" @@ -303,17 +305,16 @@ class RValueAllocation }; class RecoverWriter; // Collects snapshots in a contiguous buffer, which is copied into IonScript // memory after code generation. class SnapshotWriter { - friend class RecoverWriter; CompactBufferWriter writer_; CompactBufferWriter allocWriter_; // Map RValueAllocations to an offset in the allocWriter_ buffer. This is // useful as value allocations are repeated frequently. typedef RValueAllocation RVA; typedef HashMap<RVA, uint32_t, RVA::Hasher, SystemAllocPolicy> RValueAllocMap; RValueAllocMap allocMap_; @@ -354,27 +355,29 @@ class SnapshotWriter size_t RVATableSize() const { return allocWriter_.length(); } const uint8_t *RVATableBuffer() const { return allocWriter_.buffer(); } }; +class MResumePoint; + class RecoverWriter { CompactBufferWriter writer_; uint32_t nframes_; uint32_t framesWritten_; public: SnapshotOffset startRecover(uint32_t frameCount, bool resumeAfter); - void writeFrame(JSFunction *fun, JSScript *script, jsbytecode *pc, uint32_t exprStack); + bool writeFrame(const MResumePoint *rp); void endRecover(); size_t size() const { return writer_.length(); } const uint8_t *buffer() const { return writer_.buffer(); @@ -388,18 +391,16 @@ class RecoverWriter class RecoverReader; // A snapshot reader reads the entries out of the compressed snapshot buffer in // a script. These entries describe the equivalent interpreter frames at a given // position in JIT code. Each entry is an Ion's value allocations, used to // recover the corresponding Value from an Ion frame. class SnapshotReader { - friend class RecoverReader; - CompactBufferReader reader_; CompactBufferReader allocReader_; const uint8_t* allocTable_; BailoutKind bailoutKind_; uint32_t allocRead_; // Number of slots that have been read. RecoverOffset recoverOffset_; // Offset of the recover instructions. @@ -413,70 +414,82 @@ class SnapshotReader public: void readTrackSnapshot(); void spewBailingFrom() const; #endif private: void readSnapshotHeader(); - void readFrameHeader(); + uint32_t readAllocationIndex(); public: SnapshotReader(const uint8_t *snapshots, uint32_t offset, uint32_t RVATableSize, uint32_t listSize); RValueAllocation readAllocation(); + void skipAllocation() { + readAllocationIndex(); + } BailoutKind bailoutKind() const { return bailoutKind_; } RecoverOffset recoverOffset() const { return recoverOffset_; } + + uint32_t numAllocationsRead() const { + return allocRead_; + } + void resetNumAllocationsRead() { + allocRead_ = 0; + } }; +typedef mozilla::AlignedStorage<4 * sizeof(uint32_t)> RInstructionStorage; +class RInstruction; + class RecoverReader { CompactBufferReader reader_; - uint32_t frameCount_; - uint32_t framesRead_; // Number of frame headers that have been read. - uint32_t pcOffset_; // Offset from script->code. - uint32_t allocCount_; // Number of slots. + // Number of encoded instructions. + uint32_t numInstructions_; + + // Number of instruction read. + uint32_t numInstructionsRead_; + + // True if we need to resume after the Resume Point instruction of the + // innermost frame. bool resumeAfter_; + // Space is reserved as part of the RecoverReader to avoid allocations of + // data which is needed to decode the current instruction. + RInstructionStorage rawData_; + private: void readRecoverHeader(); - void readFrame(SnapshotReader &snapshot); + void readInstruction(); public: RecoverReader(SnapshotReader &snapshot, const uint8_t *recovers, uint32_t size); - bool moreFrames() const { - return framesRead_ < frameCount_; + bool moreInstructions() const { + return numInstructionsRead_ < numInstructions_; } - void nextFrame(SnapshotReader &snapshot) { - readFrame(snapshot); - } - uint32_t frameCount() const { - return frameCount_; + void nextInstruction() { + readInstruction(); } - uint32_t pcOffset() const { - return pcOffset_; + const RInstruction *instruction() const { + return reinterpret_cast<const RInstruction *>(rawData_.addr()); } + bool resumeAfter() const { return resumeAfter_; } - - uint32_t allocations() const { - return allocCount_; - } - bool moreAllocations(const SnapshotReader &snapshot) const { - return snapshot.allocRead_ < allocCount_; - } }; } } #endif /* jit_Snapshot_h */
--- a/js/src/jit/arm/Simulator-arm.cpp +++ b/js/src/jit/arm/Simulator-arm.cpp @@ -32,16 +32,35 @@ #include "mozilla/FloatingPoint.h" #include "mozilla/Likely.h" #include "mozilla/MathAlgorithms.h" #include "jit/arm/Assembler-arm.h" #include "jit/AsmJS.h" #include "vm/Runtime.h" +extern "C" { + +int64_t +__aeabi_idivmod(int x, int y) +{ + uint32_t lo = uint32_t(x / y); + uint32_t hi = uint32_t(x % y); + return (int64_t(hi) << 32) | lo; +} + +int64_t +__aeabi_uidivmod(int x, int y) +{ + uint32_t lo = uint32_t(x) / uint32_t(y); + uint32_t hi = uint32_t(x) % uint32_t(y); + return (int64_t(hi) << 32) | lo; +} +} + namespace js { namespace jit { // Load/store multiple addressing mode. enum BlockAddrMode { // Alias modes for comparison when writeback does not matter. da_x = (0|0|0) << 21, // Decrement after. ia_x = (0|4|0) << 21, // Increment after. @@ -4266,27 +4285,8 @@ JSRuntime::simulatorRuntime() const } void JSRuntime::setSimulatorRuntime(js::jit::SimulatorRuntime *srt) { MOZ_ASSERT(!simulatorRuntime_); simulatorRuntime_ = srt; } - -extern "C" { - -int64_t -__aeabi_idivmod(int x, int y) -{ - uint32_t lo = uint32_t(x / y); - uint32_t hi = uint32_t(x % y); - return (int64_t(hi) << 32) | lo; -} - -int64_t -__aeabi_uidivmod(int x, int y) -{ - uint32_t lo = uint32_t(x) / uint32_t(y); - uint32_t hi = uint32_t(x) % uint32_t(y); - return (int64_t(hi) << 32) | lo; -} -}
--- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -245,94 +245,44 @@ CodeGeneratorShared::encode(LRecoverInfo (void *)recover, frameCount); MResumePoint::Mode mode = recover->mir()->mode(); JS_ASSERT(mode != MResumePoint::Outer); bool resumeAfter = (mode == MResumePoint::ResumeAfter); RecoverOffset offset = recovers_.startRecover(frameCount, resumeAfter); - FlattenedMResumePointIter mirOperandIter(recover->mir()); - if (!mirOperandIter.init()) - return false; - - for (MResumePoint **it = mirOperandIter.begin(), **end = mirOperandIter.end(); + for (MResumePoint **it = recover->begin(), **end = recover->end(); it != end; ++it) { - MResumePoint *mir = *it; - MBasicBlock *block = mir->block(); - JSFunction *fun = block->info().funMaybeLazy(); - JSScript *script = block->info().script(); - jsbytecode *pc = mir->pc(); - uint32_t exprStack = mir->stackDepth() - block->info().ninvoke(); - recovers_.writeFrame(fun, script, pc, exprStack); - -#ifdef DEBUG - // Ensure that all snapshot which are encoded can safely be used for - // bailouts. - if (GetIonContext()->cx) { - uint32_t stackDepth; - bool reachablePC; - jsbytecode *bailPC = pc; - - if (mir->mode() == MResumePoint::ResumeAfter) - bailPC = GetNextPc(pc); - - if (!ReconstructStackDepth(GetIonContext()->cx, script, - bailPC, &stackDepth, &reachablePC)) - { - return false; - } - - if (reachablePC) { - if (JSOp(*bailPC) == JSOP_FUNCALL) { - // For fun.call(this, ...); the reconstructStackDepth will - // include the this. When inlining that is not included. - // So the exprStackSlots will be one less. - JS_ASSERT(stackDepth - exprStack <= 1); - } else if (JSOp(*bailPC) != JSOP_FUNAPPLY && - !IsGetPropPC(bailPC) && !IsSetPropPC(bailPC)) - { - // For fun.apply({}, arguments) the reconstructStackDepth will - // have stackdepth 4, but it could be that we inlined the - // funapply. In that case exprStackSlots, will have the real - // arguments in the slots and not be 4. - - // With accessors, we have different stack depths depending on - // whether or not we inlined the accessor, as the inlined stack - // contains a callee function that should never have been there - // and we might just be capturing an uneventful property site, in - // which case there won't have been any violence. - JS_ASSERT(exprStack == stackDepth); - } - } - } -#endif + if (!recovers_.writeFrame(*it)) + return false; } recovers_.endRecover(); recover->setRecoverOffset(offset); return !recovers_.oom(); } bool CodeGeneratorShared::encode(LSnapshot *snapshot) { if (snapshot->snapshotOffset() != INVALID_SNAPSHOT_OFFSET) return true; - if (!encode(snapshot->recoverInfo())) + LRecoverInfo *recoverInfo = snapshot->recoverInfo(); + if (!encode(recoverInfo)) return false; - RecoverOffset recoverOffset = snapshot->recoverInfo()->recoverOffset(); + RecoverOffset recoverOffset = recoverInfo->recoverOffset(); MOZ_ASSERT(recoverOffset != INVALID_RECOVER_OFFSET); - IonSpew(IonSpew_Snapshots, "Encoding LSnapshot %p (LRecoverInfo %p)", - (void *)snapshot, (void*) snapshot->recoverInfo()); + IonSpew(IonSpew_Snapshots, "Encoding LSnapshot %p (LRecover %p)", + (void *)snapshot, (void*) recoverInfo); SnapshotOffset offset = snapshots_.startSnapshot(recoverOffset, snapshot->bailoutKind()); #ifdef TRACK_SNAPSHOTS uint32_t pcOpcode = 0; uint32_t lirOpcode = 0; uint32_t lirId = 0; uint32_t mirOpcode = 0; @@ -346,22 +296,18 @@ CodeGeneratorShared::encode(LSnapshot *s mirId = ins->mirRaw()->id(); if (ins->mirRaw()->trackedPc()) pcOpcode = *ins->mirRaw()->trackedPc(); } } snapshots_.trackSnapshot(pcOpcode, mirOpcode, mirId, lirOpcode, lirId); #endif - FlattenedMResumePointIter mirOperandIter(snapshot->recoverInfo()->mir()); - if (!mirOperandIter.init()) - return false; - uint32_t startIndex = 0; - for (MResumePoint **it = mirOperandIter.begin(), **end = mirOperandIter.end(); + for (MResumePoint **it = recoverInfo->begin(), **end = recoverInfo->end(); it != end; ++it) { MResumePoint *mir = *it; if (!encodeAllocations(snapshot, mir, &startIndex)) return false; }
--- a/js/src/jit/shared/Lowering-shared.cpp +++ b/js/src/jit/shared/Lowering-shared.cpp @@ -58,38 +58,37 @@ LIRGeneratorShared::lowerTypedPhiInput(M LRecoverInfo * LIRGeneratorShared::getRecoverInfo(MResumePoint *rp) { if (cachedRecoverInfo_ && cachedRecoverInfo_->mir() == rp) return cachedRecoverInfo_; LRecoverInfo *recoverInfo = LRecoverInfo::New(gen, rp); + if (!recoverInfo) + return nullptr; + cachedRecoverInfo_ = recoverInfo; return recoverInfo; } #ifdef JS_NUNBOX32 LSnapshot * LIRGeneratorShared::buildSnapshot(LInstruction *ins, MResumePoint *rp, BailoutKind kind) { LRecoverInfo *recover = getRecoverInfo(rp); if (!recover) return nullptr; LSnapshot *snapshot = LSnapshot::New(gen, recover, kind); if (!snapshot) return nullptr; - FlattenedMResumePointIter iter(rp); - if (!iter.init()) - return nullptr; - size_t i = 0; - for (MResumePoint **it = iter.begin(), **end = iter.end(); it != end; ++it) { + for (MResumePoint **it = recover->begin(), **end = recover->end(); it != end; ++it) { MResumePoint *mir = *it; for (size_t j = 0, e = mir->numOperands(); j < e; ++i, ++j) { MDefinition *ins = mir->getOperand(j); LAllocation *type = snapshot->typeOfSlot(i); LAllocation *payload = snapshot->payloadOfSlot(i); if (ins->isBox()) @@ -132,22 +131,18 @@ LIRGeneratorShared::buildSnapshot(LInstr LRecoverInfo *recover = getRecoverInfo(rp); if (!recover) return nullptr; LSnapshot *snapshot = LSnapshot::New(gen, recover, kind); if (!snapshot) return nullptr; - FlattenedMResumePointIter iter(rp); - if (!iter.init()) - return nullptr; - size_t i = 0; - for (MResumePoint **it = iter.begin(), **end = iter.end(); it != end; ++it) { + for (MResumePoint **it = recover->begin(), **end = recover->end(); it != end; ++it) { MResumePoint *mir = *it; for (size_t j = 0, e = mir->numOperands(); j < e; ++i, ++j) { MDefinition *def = mir->getOperand(j); if (def->isBox()) def = def->toBox()->getOperand(0); // Guards should never be eliminated.
--- a/js/src/moz.build +++ b/js/src/moz.build @@ -276,16 +276,17 @@ if CONFIG['ENABLE_ION']: 'jit/MCallOptimize.cpp', 'jit/MIR.cpp', 'jit/MIRGraph.cpp', 'jit/MoveResolver.cpp', 'jit/ParallelFunctions.cpp', 'jit/ParallelSafetyAnalysis.cpp', 'jit/PerfSpewer.cpp', 'jit/RangeAnalysis.cpp', + 'jit/Recover.cpp', 'jit/RegisterAllocator.cpp', 'jit/Safepoints.cpp', 'jit/shared/BaselineCompiler-shared.cpp', 'jit/shared/CodeGenerator-shared.cpp', 'jit/shared/Lowering-shared.cpp', 'jit/Snapshots.cpp', 'jit/StupidAllocator.cpp', 'jit/TypeDescrSet.cpp',
--- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -1371,17 +1371,17 @@ FrameIter::numFrameSlots() const { switch (data_.state_) { case DONE: case ASMJS: break; case JIT: { #ifdef JS_ION if (data_.ionFrames_.isIonJS()) { - return ionInlineFrames_.snapshotIterator().allocations() - + return ionInlineFrames_.snapshotIterator().numAllocations() - ionInlineFrames_.script()->nfixed(); } jit::BaselineFrame *frame = data_.ionFrames_.baselineFrame(); return frame->numValueSlots() - data_.ionFrames_.script()->nfixed(); #else break; #endif }
--- a/js/xpconnect/src/XPCQuickStubs.h +++ b/js/xpconnect/src/XPCQuickStubs.h @@ -501,22 +501,16 @@ castNativeArgFromWrapper(JSContext *cx, } inline nsWrapperCache* xpc_qsGetWrapperCache(nsWrapperCache *cache) { return cache; } -// nsGlobalWindow implements nsWrapperCache, but doesn't always use it. Don't -// try to use it without fixing that first. -class nsGlobalWindow; -inline nsWrapperCache* -xpc_qsGetWrapperCache(nsGlobalWindow *not_allowed); - inline nsWrapperCache* xpc_qsGetWrapperCache(void *p) { return nullptr; } /** Convert an XPCOM pointer to jsval. Return true on success. * aIdentity is a performance optimization. Set it to true,
--- a/js/xpconnect/src/XPCWrappedNative.cpp +++ b/js/xpconnect/src/XPCWrappedNative.cpp @@ -135,17 +135,16 @@ FinishCreate(XPCWrappedNativeScope* Scop // and finally into XPCWrappedNative::Init. Unfortunately, this path assumes // very early on that we have an XPCWrappedNativeScope and corresponding global // JS object, which are the very things we need to create here. So we special- // case the logic and do some things in a different order. nsresult XPCWrappedNative::WrapNewGlobal(xpcObjectHelper &nativeHelper, nsIPrincipal *principal, bool initStandardClasses, - bool fireOnNewGlobalHook, JS::CompartmentOptions& aOptions, XPCWrappedNative **wrappedGlobal) { AutoJSContext cx; nsISupports *identity = nativeHelper.GetCanonical(); // The object should specify that it's meant to be global. MOZ_ASSERT(nativeHelper.GetScriptableFlags() & nsIXPCScriptable::IS_GLOBAL_OBJECT); @@ -265,18 +264,16 @@ XPCWrappedNative::WrapNewGlobal(xpcObjec // Call the common creation finish routine. This does all of the bookkeeping // like inserting the wrapper into the wrapper map and setting up the wrapper // cache. nsresult rv = FinishCreate(scope, iface, nativeHelper.GetWrapperCache(), wrapper, wrappedGlobal); NS_ENSURE_SUCCESS(rv, rv); - if (fireOnNewGlobalHook) - JS_FireOnNewGlobalObject(cx, global); return NS_OK; } // static nsresult XPCWrappedNative::GetNewOrUsed(xpcObjectHelper& helper, XPCWrappedNativeScope* Scope, XPCNativeInterface* Interface,
--- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -286,17 +286,18 @@ xpc_TryUnmarkWrappedGrayObject(nsISuppor static_cast<nsXPCWrappedJS*>(wjs.get())->GetJSObject(); } } /***************************************************************************/ /***************************************************************************/ // nsIXPConnect interface methods... -static inline nsresult UnexpectedFailure(nsresult rv) +template<typename T> +static inline T UnexpectedFailure(T rv) { NS_ERROR("This is not supposed to fail!"); return rv; } /* void initClasses (in JSContextPtr aJSContext, in JSObjectPtr aGlobalJSObj); */ NS_IMETHODIMP nsXPConnect::InitClasses(JSContext * aJSContext, JSObject * aGlobalJSObj) @@ -393,16 +394,50 @@ CreateGlobalObject(JSContext *cx, const strcmp(className, "ChromeWindow") == 0) ? ProtoAndIfaceCache::WindowLike : ProtoAndIfaceCache::NonWindowLike); } return global; } +bool +InitGlobalObject(JSContext* aJSContext, JS::Handle<JSObject*> aGlobal, uint32_t aFlags) +{ + // Immediately enter the global's compartment, so that everything else we + // create ends up there. + JSAutoCompartment ac(aJSContext, aGlobal); + if (!(aFlags & nsIXPConnect::OMIT_COMPONENTS_OBJECT)) { + // XPCCallContext gives us an active request needed to save/restore. + if (!GetCompartmentPrivate(aGlobal)->scope->AttachComponentsObject(aJSContext) || + !XPCNativeWrapper::AttachNewConstructorObject(aJSContext, aGlobal)) { + return UnexpectedFailure(false); + } + } + + // Stuff coming through this path always ends up as a DOM global. + MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL); + + // Init WebIDL binding constructors wanted on all XPConnect globals. + // + // XXX Please do not add any additional classes here without the approval of + // the XPConnect module owner. + if (!PromiseBinding::GetConstructorObject(aJSContext, aGlobal) || + !TextDecoderBinding::GetConstructorObject(aJSContext, aGlobal) || + !TextEncoderBinding::GetConstructorObject(aJSContext, aGlobal) || + !DOMErrorBinding::GetConstructorObject(aJSContext, aGlobal)) { + return UnexpectedFailure(false); + } + + if (!(aFlags & nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK)) + JS_FireOnNewGlobalObject(aJSContext, aGlobal); + + return true; +} + } // namespace xpc NS_IMETHODIMP nsXPConnect::InitClassesWithNewWrappedGlobal(JSContext * aJSContext, nsISupports *aCOMObj, nsIPrincipal * aPrincipal, uint32_t aFlags, JS::CompartmentOptions& aOptions, @@ -419,49 +454,25 @@ nsXPConnect::InitClassesWithNewWrappedGl // Call into XPCWrappedNative to make a new global object, scope, and global // prototype. xpcObjectHelper helper(aCOMObj); MOZ_ASSERT(helper.GetScriptableFlags() & nsIXPCScriptable::IS_GLOBAL_OBJECT); nsRefPtr<XPCWrappedNative> wrappedGlobal; nsresult rv = XPCWrappedNative::WrapNewGlobal(helper, aPrincipal, aFlags & nsIXPConnect::INIT_JS_STANDARD_CLASSES, - !(aFlags & nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK), aOptions, getter_AddRefs(wrappedGlobal)); NS_ENSURE_SUCCESS(rv, rv); // Grab a copy of the global and enter its compartment. RootedObject global(aJSContext, wrappedGlobal->GetFlatJSObject()); MOZ_ASSERT(!js::GetObjectParent(global)); - JSAutoCompartment ac(aJSContext, global); - if (!(aFlags & nsIXPConnect::OMIT_COMPONENTS_OBJECT)) { - // XPCCallContext gives us an active request needed to save/restore. - if (!wrappedGlobal->GetScope()->AttachComponentsObject(aJSContext)) - return UnexpectedFailure(NS_ERROR_FAILURE); - - if (!XPCNativeWrapper::AttachNewConstructorObject(aJSContext, global)) - return UnexpectedFailure(NS_ERROR_FAILURE); - } - - // Stuff coming through this path always ends up as a DOM global. - // XXX Someone who knows why we can assert this should re-check - // (after bug 720580). - MOZ_ASSERT(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL); - - // Init WebIDL binding constructors wanted on all XPConnect globals. - // - // XXX Please do not add any additional classes here without the approval of - // the XPConnect module owner. - if (!PromiseBinding::GetConstructorObject(aJSContext, global) || - !TextDecoderBinding::GetConstructorObject(aJSContext, global) || - !TextEncoderBinding::GetConstructorObject(aJSContext, global) || - !DOMErrorBinding::GetConstructorObject(aJSContext, global)) { + if (!InitGlobalObject(aJSContext, global, aFlags)) return UnexpectedFailure(NS_ERROR_FAILURE); - } wrappedGlobal.forget(_retval); return NS_OK; } static nsresult NativeInterface2JSObject(HandleObject aScope, nsISupports *aCOMObj,
--- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -2042,17 +2042,16 @@ public: XPCJSRuntime* GetRuntime() const {XPCWrappedNativeScope* scope = GetScope(); return scope ? scope->GetRuntime() : nullptr;} static nsresult WrapNewGlobal(xpcObjectHelper &nativeHelper, nsIPrincipal *principal, bool initStandardClasses, - bool fireOnNewGlobalHook, JS::CompartmentOptions& aOptions, XPCWrappedNative **wrappedGlobal); static nsresult GetNewOrUsed(xpcObjectHelper& helper, XPCWrappedNativeScope* Scope, XPCNativeInterface* Interface, XPCWrappedNative** wrapper); @@ -3393,16 +3392,20 @@ public: JS::RootedId defineAs; }; JSObject * CreateGlobalObject(JSContext *cx, const JSClass *clasp, nsIPrincipal *principal, JS::CompartmentOptions& aOptions); +bool +InitGlobalObject(JSContext* aJSContext, JS::Handle<JSObject*> aGlobal, + uint32_t aFlags); + // Helper for creating a sandbox object to use for evaluating // untrusted code completely separated from all other code in the // system using EvalInSandbox(). Takes the JSContext on which to // do setup etc on, puts the sandbox object in *vp (which must be // rooted by the caller), and uses the principal that's either // directly passed in prinOrSop or indirectly as an // nsIScriptObjectPrincipal holding the principal. If no principal is // reachable through prinOrSop, a new null principal will be created
--- a/js/xpconnect/src/xpcpublic.h +++ b/js/xpconnect/src/xpcpublic.h @@ -19,16 +19,17 @@ #include "nsWrapperCache.h" #include "nsStringGlue.h" #include "nsTArray.h" #include "mozilla/dom/JSSlots.h" #include "nsMathUtils.h" #include "nsStringBuffer.h" #include "mozilla/dom/BindingDeclarations.h" +class nsGlobalWindow; class nsIPrincipal; class nsScriptNameSpaceManager; class nsIGlobalObject; class nsIMemoryReporterCallback; #ifndef BAD_TLS_INDEX #define BAD_TLS_INDEX ((uint32_t) -1) #endif
--- a/js/xpconnect/tests/chrome/test_doublewrappedcompartments.xul +++ b/js/xpconnect/tests/chrome/test_doublewrappedcompartments.xul @@ -17,30 +17,26 @@ https://bugzilla.mozilla.org/show_bug.cg src="http://example.org/tests/js/xpconnect/tests/mochitest/file_doublewrappedcompartments.html" onload="go()" id="ifr"> </iframe> </body> <!-- test code goes here --> <script type="application/javascript"><![CDATA[ - const Ci = Components.interfaces; - const utils = window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); + const utils = SpecialPowers.DOMWindowUtils; function go() { var wrappedWin = $('ifr').contentWindow; is(typeof wrappedWin.expando, 'undefined', "Shouldn't be able to see the expando"); var unwrapped = wrappedWin.wrappedJSObject; var expando = unwrapped.expando; - is(utils.getClassName(expando), 'Proxy', 'properly wrapped'); + isnot(expando, 'undefined', 'properly wrapped'); is(typeof expando.querySelector, 'function', 'double wrapped'); - ok(unwrapped.testme(expando), - "content didn't get a proxy, but another double wrapped object"); SimpleTest.finish(); } SimpleTest.waitForExplicitFinish(); ]]></script> </window>
--- a/js/xpconnect/tests/mochitest/file_doublewrappedcompartments.html +++ b/js/xpconnect/tests/mochitest/file_doublewrappedcompartments.html @@ -1,20 +1,10 @@ <html> <head> <script> // We want to put an expando on the object, but we want this object // to be wrapped in other compartments. This means that the expando // must implement precreate, which happens (in general) for nodes. // So we just do a cyclic reference to the document body. window.expando = document.documentElement; - - function testme(obj) { - netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); - const Ci = Components.interfaces; - const utils = window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - - return utils.getClassName(obj) != "Proxy" && - typeof obj.querySelector == 'function'; - } </script> </head>
--- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -1449,26 +1449,28 @@ DOMXrayTraits::resolveOwnProperty(JSCont return true; } bool DOMXrayTraits::defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, MutableHandle<JSPropertyDescriptor> desc, Handle<JSPropertyDescriptor> existingDesc, bool *defined) { - if (!existingDesc.object()) - return true; - // Check for an indexed property on a Window. If that's happening, do // nothing but claim we defined it so it won't get added as an expando. - int32_t index = GetArrayIndexFromId(cx, id); - if (IsArrayIndex(index) && IsWindow(cx, wrapper)) { - *defined = true; + if (IsWindow(cx, wrapper)) { + int32_t index = GetArrayIndexFromId(cx, id); + if (IsArrayIndex(index)) { + *defined = true; + return true; + } + } + + if (!existingDesc.object()) return true; - } JS::Rooted<JSObject*> obj(cx, getTargetObject(wrapper)); return XrayDefineProperty(cx, wrapper, obj, id, desc, defined); } bool DOMXrayTraits::enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags, AutoIdVector &props)
--- a/layout/mathml/nsIMathMLFrame.h +++ b/layout/mathml/nsIMathMLFrame.h @@ -195,16 +195,23 @@ public: uint32_t aWhichFlags) = 0; // If aFrame is a child frame, returns the script increment which this frame // imposes on the specified frame, ignoring any artificial adjustments to // scriptlevel. // Returns 0 if the specified frame isn't a child frame. virtual uint8_t ScriptIncrement(nsIFrame* aFrame) = 0; + + // Returns true if the frame is considered to be an mrow for layout purposes. + // This includes inferred mrows, but excludes <mrow> elements with a single + // child. In the latter case, the child is to be treated as if it wasn't + // within an mrow, so we pretend the mrow isn't mrow-like. + virtual bool + IsMrowLike() = 0; }; // struct used by a container frame to keep track of its embellishments. // By convention, the data that we keep here is bubbled from the embellished // hierarchy, and it remains unchanged unless we have to recover from a change // that occurs in the embellished hierarchy. The struct remains in its nil // state in those frames that are not part of the embellished hierarchy. struct nsEmbellishData {
--- a/layout/mathml/nsMathMLContainerFrame.cpp +++ b/layout/mathml/nsMathMLContainerFrame.cpp @@ -1595,15 +1595,23 @@ NS_NewMathMLmathBlockFrame(nsIPresShell* { nsMathMLmathBlockFrame* it = new (aPresShell) nsMathMLmathBlockFrame(aContext); it->SetFlags(aFlags); return it; } NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmathBlockFrame) +NS_QUERYFRAME_HEAD(nsMathMLmathBlockFrame) + NS_QUERYFRAME_ENTRY(nsMathMLmathBlockFrame) +NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame) + nsIFrame* NS_NewMathMLmathInlineFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { return new (aPresShell) nsMathMLmathInlineFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmathInlineFrame) + +NS_QUERYFRAME_HEAD(nsMathMLmathInlineFrame) + NS_QUERYFRAME_ENTRY(nsIMathMLFrame) +NS_QUERYFRAME_TAIL_INHERITING(nsInlineFrame)
--- a/layout/mathml/nsMathMLContainerFrame.h +++ b/layout/mathml/nsMathMLContainerFrame.h @@ -403,16 +403,18 @@ private: // simply mapping to nsBlockFrame or nsInlineFrame. // A separate implemention needs to provide: // 1) line-breaking // 2) proper inter-frame spacing // 3) firing of Stretch() (in which case FinalizeReflow() would have to be cleaned) // Issues: If/when mathml becomes a pluggable component, the separation will be needed. class nsMathMLmathBlockFrame : public nsBlockFrame { public: + NS_DECL_QUERYFRAME_TARGET(nsMathMLmathBlockFrame) + NS_DECL_QUERYFRAME NS_DECL_FRAMEARENA_HELPERS friend nsIFrame* NS_NewMathMLmathBlockFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, nsFrameState aFlags); // beware, mFrames is not set by nsBlockFrame // cannot use mFrames{.FirstChild()|.etc} since the block code doesn't set mFrames virtual nsresult @@ -463,29 +465,38 @@ public: return rv; } virtual bool IsFrameOfType(uint32_t aFlags) const MOZ_OVERRIDE { return nsBlockFrame::IsFrameOfType(aFlags & ~(nsIFrame::eMathML | nsIFrame::eExcludesIgnorableWhitespace)); } + // See nsIMathMLFrame.h + bool IsMrowLike() { + return mFrames.FirstChild() != mFrames.LastChild() || + !mFrames.FirstChild(); + } + protected: nsMathMLmathBlockFrame(nsStyleContext* aContext) : nsBlockFrame(aContext) { // We should always have a float manager. Not that things can really try // to float out of us anyway, but we need one for line layout. AddStateBits(NS_BLOCK_FLOAT_MGR); } virtual ~nsMathMLmathBlockFrame() {} }; // -------------- -class nsMathMLmathInlineFrame : public nsInlineFrame { +class nsMathMLmathInlineFrame : public nsInlineFrame, + public nsMathMLFrame { public: + NS_DECL_QUERYFRAME_TARGET(nsMathMLmathInlineFrame) + NS_DECL_QUERYFRAME NS_DECL_FRAMEARENA_HELPERS friend nsIFrame* NS_NewMathMLmathInlineFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); virtual nsresult SetInitialChildList(ChildListID aListID, nsFrameList& aChildList) MOZ_OVERRIDE { @@ -533,14 +544,20 @@ public: return rv; } virtual bool IsFrameOfType(uint32_t aFlags) const MOZ_OVERRIDE { return nsInlineFrame::IsFrameOfType(aFlags & ~(nsIFrame::eMathML | nsIFrame::eExcludesIgnorableWhitespace)); } + bool + IsMrowLike() MOZ_OVERRIDE { + return mFrames.FirstChild() != mFrames.LastChild() || + !mFrames.FirstChild(); + } + protected: nsMathMLmathInlineFrame(nsStyleContext* aContext) : nsInlineFrame(aContext) {} virtual ~nsMathMLmathInlineFrame() {} }; #endif /* nsMathMLContainerFrame_h___ */
--- a/layout/mathml/nsMathMLFrame.h +++ b/layout/mathml/nsMathMLFrame.h @@ -93,16 +93,22 @@ public: } uint8_t ScriptIncrement(nsIFrame* aFrame) MOZ_OVERRIDE { return 0; } + bool + IsMrowLike() MOZ_OVERRIDE + { + return false; + } + // helper to give a style context suitable for doing the stretching to the // MathMLChar. Frame classes that use this should make the extra style contexts // accessible to the Style System via Get/Set AdditionalStyleContext. static void ResolveMathMLCharStyle(nsPresContext* aPresContext, nsIContent* aContent, nsStyleContext* aParenStyleContext, nsMathMLChar* aMathMLChar,
--- a/layout/mathml/nsMathMLmencloseFrame.h +++ b/layout/mathml/nsMathMLmencloseFrame.h @@ -76,16 +76,22 @@ public: InheritAutomaticData(nsIFrame* aParent) MOZ_OVERRIDE; NS_IMETHOD TransmitAutomaticData() MOZ_OVERRIDE; virtual nscoord FixInterFrameSpacing(nsHTMLReflowMetrics& aDesiredSize) MOZ_OVERRIDE; + bool + IsMrowLike() MOZ_OVERRIDE { + return mFrames.FirstChild() != mFrames.LastChild() || + !mFrames.FirstChild(); + } + protected: nsMathMLmencloseFrame(nsStyleContext* aContext); virtual ~nsMathMLmencloseFrame(); nsresult PlaceInternal(nsRenderingContext& aRenderingContext, bool aPlaceOrigin, nsHTMLReflowMetrics& aDesiredSize, bool aWidthOnly);
--- a/layout/mathml/nsMathMLmfencedFrame.h +++ b/layout/mathml/nsMathMLmfencedFrame.h @@ -75,16 +75,27 @@ public: bool aRTL); static void PlaceChar(nsMathMLChar* aMathMLChar, nscoord aDesiredSize, nsBoundingMetrics& bm, nscoord& dx); + virtual bool + IsMrowLike() MOZ_OVERRIDE + { + // Always treated as an mrow with > 1 child as + // <mfenced> <mo>%</mo> </mfenced> + // renders equivalently to + // <mrow> <mo> ( </mo> <mo>%</mo> <mo> ) </mo> </mrow> + // This also holds with multiple children. (MathML3 3.3.8.1) + return true; + } + protected: nsMathMLmfencedFrame(nsStyleContext* aContext) : nsMathMLContainerFrame(aContext) {} virtual ~nsMathMLmfencedFrame(); nsMathMLChar* mOpenChar; nsMathMLChar* mCloseChar; nsMathMLChar* mSeparatorsChar; int32_t mSeparatorsCount;
--- a/layout/mathml/nsMathMLmoFrame.cpp +++ b/layout/mathml/nsMathMLmoFrame.cpp @@ -305,21 +305,33 @@ nsMathMLmoFrame::ProcessOperatorData() mFlags &= ~NS_MATHML_OPERATOR_EMBELLISH_ANCESTOR; // find the position of our outermost embellished container w.r.t // its siblings. nsIFrame* nextSibling = embellishAncestor->GetNextSibling(); nsIFrame* prevSibling = embellishAncestor->GetPrevSibling(); - // flag to distinguish from a real infix - if (!prevSibling && !nextSibling) + // flag to distinguish from a real infix. Set for (embellished) operators + // that live in (inferred) mrows. + nsIMathMLFrame* mathAncestor = do_QueryFrame(parentAncestor); + bool zeroSpacing = false; + if (mathAncestor) { + zeroSpacing = !mathAncestor->IsMrowLike(); + } else { + nsMathMLmathBlockFrame* blockFrame = do_QueryFrame(parentAncestor); + if (blockFrame) { + zeroSpacing = !blockFrame->IsMrowLike(); + } + } + if (zeroSpacing) { mFlags |= NS_MATHML_OPERATOR_EMBELLISH_ISOLATED; - else + } else { mFlags &= ~NS_MATHML_OPERATOR_EMBELLISH_ISOLATED; + } // find our form form = NS_MATHML_OPERATOR_FORM_INFIX; mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::form, value); if (!value.IsEmpty()) { if (value.EqualsLiteral("prefix")) form = NS_MATHML_OPERATOR_FORM_PREFIX; else if (value.EqualsLiteral("postfix")) @@ -339,40 +351,37 @@ nsMathMLmoFrame::ProcessOperatorData() // http://www.w3.org/TR/MathML/chapter3.html#presm.mo.attrs // thickmathspace = 5/18em float lspace = 5.0f/18.0f; float rspace = 5.0f/18.0f; // lookup the operator dictionary nsAutoString data; mMathMLChar.GetData(data); nsMathMLOperators::LookupOperator(data, form, &mFlags, &lspace, &rspace); - if (lspace || rspace) { + // Spacing is zero if our outermost embellished operator is not in an + // inferred mrow. + if (!NS_MATHML_OPERATOR_EMBELLISH_IS_ISOLATED(mFlags) && + (lspace || rspace)) { // Cache the default values of lspace and rspace. // since these values are relative to the 'em' unit, convert to twips now nscoord em; nsRefPtr<nsFontMetrics> fm; nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm)); GetEmHeight(fm, em); mEmbellishData.leadingSpace = NSToCoordRound(lspace * em); mEmbellishData.trailingSpace = NSToCoordRound(rspace * em); // tuning if we don't want too much extra space when we are a script. // (with its fonts, TeX sets lspace=0 & rspace=0 as soon as scriptlevel>0. // Our fonts can be anything, so...) - if (StyleFont()->mScriptLevel > 0) { - if (NS_MATHML_OPERATOR_EMBELLISH_IS_ISOLATED(mFlags)) { - // could be an isolated accent or script, e.g., x^{+}, just zero out - mEmbellishData.leadingSpace = 0; - mEmbellishData.trailingSpace = 0; - } - else if (!NS_MATHML_OPERATOR_HAS_EMBELLISH_ANCESTOR(mFlags)) { - mEmbellishData.leadingSpace /= 2; - mEmbellishData.trailingSpace /= 2; - } + if (StyleFont()->mScriptLevel > 0 && + !NS_MATHML_OPERATOR_HAS_EMBELLISH_ANCESTOR(mFlags)) { + mEmbellishData.leadingSpace /= 2; + mEmbellishData.trailingSpace /= 2; } } } // If we are an accent without explicit lspace="." or rspace=".", // we will ignore our default leading/trailing space // lspace
--- a/layout/mathml/nsMathMLmpaddedFrame.h +++ b/layout/mathml/nsMathMLmpaddedFrame.h @@ -33,16 +33,22 @@ public: const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) MOZ_OVERRIDE; virtual nsresult Place(nsRenderingContext& aRenderingContext, bool aPlaceOrigin, nsHTMLReflowMetrics& aDesiredSize) MOZ_OVERRIDE; + bool + IsMrowLike() MOZ_OVERRIDE { + return mFrames.FirstChild() != mFrames.LastChild() || + !mFrames.FirstChild(); + } + protected: nsMathMLmpaddedFrame(nsStyleContext* aContext) : nsMathMLContainerFrame(aContext) {} virtual ~nsMathMLmpaddedFrame(); virtual nsresult MeasureForWidth(nsRenderingContext& aRenderingContext, nsHTMLReflowMetrics& aDesiredSize) MOZ_OVERRIDE;
--- a/layout/mathml/nsMathMLmphantomFrame.h +++ b/layout/mathml/nsMathMLmphantomFrame.h @@ -26,15 +26,21 @@ public: TransmitAutomaticData() MOZ_OVERRIDE { return TransmitAutomaticDataForMrowLikeElement(); } virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect, const nsDisplayListSet& aLists) MOZ_OVERRIDE {} + bool + IsMrowLike() MOZ_OVERRIDE { + return mFrames.FirstChild() != mFrames.LastChild() || + !mFrames.FirstChild(); + } + protected: nsMathMLmphantomFrame(nsStyleContext* aContext) : nsMathMLContainerFrame(aContext) {} virtual ~nsMathMLmphantomFrame(); }; #endif /* nsMathMLmphantomFrame_h___ */
--- a/layout/mathml/nsMathMLmrowFrame.h +++ b/layout/mathml/nsMathMLmrowFrame.h @@ -27,14 +27,23 @@ public: NS_IMETHOD InheritAutomaticData(nsIFrame* aParent) MOZ_OVERRIDE; NS_IMETHOD TransmitAutomaticData() MOZ_OVERRIDE { return TransmitAutomaticDataForMrowLikeElement(); } + bool + IsMrowLike() MOZ_OVERRIDE { + // <mrow> elements with a single child are treated identically to the case + // where the child wasn't within an mrow, so we pretend the mrow isn't an + // mrow in that situation. + return mFrames.FirstChild() != mFrames.LastChild() || + !mFrames.FirstChild(); + } + protected: nsMathMLmrowFrame(nsStyleContext* aContext) : nsMathMLContainerFrame(aContext) {} virtual ~nsMathMLmrowFrame(); }; #endif /* nsMathMLmrowFrame_h___ */
--- a/layout/mathml/nsMathMLmsqrtFrame.h +++ b/layout/mathml/nsMathMLmsqrtFrame.h @@ -47,15 +47,22 @@ public: NS_IMETHOD InheritAutomaticData(nsIFrame* aParent) MOZ_OVERRIDE; virtual nsresult AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, int32_t aModType) MOZ_OVERRIDE; + virtual bool + IsMrowLike() MOZ_OVERRIDE + { + return mFrames.FirstChild() != mFrames.LastChild() || + !mFrames.FirstChild(); + } + protected: nsMathMLmsqrtFrame(nsStyleContext* aContext); virtual ~nsMathMLmsqrtFrame(); }; #endif /* nsMathMLmsqrtFrame_h___ */
--- a/layout/mathml/nsMathMLmtableFrame.h +++ b/layout/mathml/nsMathMLmtableFrame.h @@ -246,16 +246,22 @@ public: { return nsBlockFrame::IsFrameOfType(aFlags & ~(nsIFrame::eMathML | nsIFrame::eExcludesIgnorableWhitespace)); } virtual const nsStyleText* StyleTextForLineLayout() MOZ_OVERRIDE; virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) MOZ_OVERRIDE; + bool + IsMrowLike() MOZ_OVERRIDE { + return mFrames.FirstChild() != mFrames.LastChild() || + !mFrames.FirstChild(); + } + protected: nsMathMLmtdInnerFrame(nsStyleContext* aContext); virtual ~nsMathMLmtdInnerFrame(); nsStyleText* mUniqueStyleText; }; // class nsMathMLmtdInnerFrame
--- a/layout/reftests/font-face/local-1-ref.html +++ b/layout/reftests/font-face/local-1-ref.html @@ -3,17 +3,17 @@ <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta http-equiv="Content-Style-Type" content="text/css"> <title>test src: local() reference</title> <style type="text/css"> body { font-family: Nimbus Sans L, Helvetica, Bitstream Vera Sans, Arial, Liberation Sans, SwissA, - Droid Sans, Roboto, serif; + Fira Sans OT, Fira Sans, Droid Sans, Roboto, serif; } </style> </head> <body> <p style="font-weight: normal;"> The quick brown fox jumped over the lazy dog </p> <p style="font-weight: bold;">
--- a/layout/reftests/font-face/local-1.html +++ b/layout/reftests/font-face/local-1.html @@ -30,25 +30,27 @@ --> <style type="text/css"> @font-face { font-family: "Local"; src: local(Nimbus Sans L), local(NimbusSansL-Regu), local(Helvetica), local(Bitstream Vera Sans), local(Arial), local(Liberation Sans), local(SwissA), + local(Fira Sans OT), local(Fira Sans), local(Droid Sans), local(Roboto); font-weight: 100; } @font-face { font-family: "Local"; src: local(Nimbus Sans L Bold), local(NimbusSansL-Bold), local(Helvetica Bold), local(Helvetica-Bold), local(Bitstream Vera Sans Bold), local(Arial Bold), local(Liberation Sans Bold), local(SwissA Bold), + local(Fira Sans OT Bold), local(Fira Sans Bold), local(Droid Sans Bold), local(Roboto Bold); font-weight: normal; } body { font-family: Local, serif } </style> </head> <body> <p style="font-weight: 100">
--- a/layout/reftests/font-face/local-styled-1-ref.html +++ b/layout/reftests/font-face/local-styled-1-ref.html @@ -2,16 +2,17 @@ <html> <head> <style type="text/css"> @font-face { font-family: test; src: local(Nimbus Sans L), local(NimbusSansL-Regu), local(Helvetica), local(Bitstream Vera Sans), local(Arial), local(Liberation Sans), local(SwissA), + local(Fira Sans OT), local(Fira Sans), local(Droid Sans), local(Roboto); } div { font-family: test, serif; margin: 10px; } </style> </head>
--- a/layout/reftests/font-face/local-styled-1.html +++ b/layout/reftests/font-face/local-styled-1.html @@ -2,32 +2,35 @@ <html> <head> <style type="text/css"> @font-face { font-family: test; src: local(Nimbus Sans L), local(NimbusSansL-Regu), local(Helvetica), local(Bitstream Vera Sans), local(Arial), local(Liberation Sans), local(SwissA), + local(Fira Sans OT), local(Fira Sans), local(Droid Sans), local(Roboto); } @font-face { font-family: test; font-style: italic; src: local(Nimbus Sans L), local(NimbusSansL-Regu), local(Helvetica), local(Bitstream Vera Sans), local(Arial), local(Liberation Sans), local(SwissA), + local(Fira Sans OT), local(Fira Sans), local(Droid Sans), local(Roboto); } @font-face { font-family: test; font-weight: bold; src: local(Nimbus Sans L), local(NimbusSansL-Regu), local(Helvetica), local(Bitstream Vera Sans), local(Arial), local(Liberation Sans), local(SwissA), + local(Fira Sans OT), local(Fira Sans), local(Droid Sans), local(Roboto); } div { font-family: test, serif; margin: 10px; } </style> </head>
--- a/layout/reftests/font-face/reftest.list +++ b/layout/reftests/font-face/reftest.list @@ -87,19 +87,18 @@ HTTP(..) != media-query-add-1-ref.html m HTTP(..) == ahem-metrics-1.html ahem-metrics-1-ref.html HTTP(..) == ex-unit-1.html ex-unit-1-ref.html HTTP(..) == ex-unit-1-dynamic.html ex-unit-1-ref.html # bug 493976 - for some reason the Arabic tests below cause Tinderbox timeouts # HTTP(..) == src-format-arabic.html src-format-arabic-ot-ref.html -# bug 769194 - src:local() completely broken on android -skip-if(B2G) fails-if(Android) == local-1.html local-1-ref.html # bug 773482 -skip-if(B2G) fails-if(Android) == local-styled-1.html local-styled-1-ref.html # bug 773482 +== local-1.html local-1-ref.html +== local-styled-1.html local-styled-1-ref.html skip-if(B2G) HTTP(..) == synthetic-weight-style.html synthetic-weight-style-ref.html # bug 773482 skip-if(B2G) HTTP(..) == synthetic-variations.html synthetic-variations-ref.html # Leak test HTTP(..) load 486974-1.html # compare fonts with and without bad head checksum
new file mode 100644 --- /dev/null +++ b/layout/reftests/mathml/mo-lspace-rspace-2-ref.html @@ -0,0 +1,379 @@ +<!DOCTYPE html> +<html> + <head> + <title>mo-lspace-rspace</title> + <style type="text/css"> + mo,td { + background-color: red; + } + msub, mfrac, msup, msubsup, mmultiscripts, + mover, munder, munderover, mpadded, merror + { + background-color: blue + } + math[display] + { + background-color: blue + } + </style> + </head> + <body> + + <p> + No lspace or rspace added: + </p> + <p> + <math> + <mrow> + <mo lspace="0" rspace="0">%</mo> + </mrow> + </math> + <math> + <mfrac> + <mo lspace="0" rspace="0">%</mo> + <mo lspace="0" rspace="0">%</mo> + </mfrac> + </math> + <math> + <msqrt> + <mo lspace="0" rspace="0">%</mo> + </msqrt> + </math> + <math> + <mroot> + <mo lspace="0" rspace="0">%</mo> + <mo lspace="0" rspace="0">%</mo> + </mroot> + </math> + <math> + <mstyle> + <mo lspace="0" rspace="0">%</mo> + </mstyle> + </math> + </p> + + <p> + <table> + <tr> + <td> + <math> + <mphantom> + <mo lspace="0" rspace="0">%</mo> + </mphantom> + </math> + </td> + </tr> + </table> + </p> + + <p> + <math> + <menclose notation="circle"> + <mo lspace="0" rspace="0">%</mo> + </menclose> + </math> + <math> + <msub> + <mo lspace="0" rspace="0">%</mo> + <mo lspace="0" rspace="0">%</mo> + </msub> + </math> + <math> + <msup> + <mo lspace="0" rspace="0">%</mo> + <mo lspace="0" rspace="0">%</mo> + </msup> + </math> + <math> + <msubsup> + <mo lspace="0" rspace="0">%</mo> + <mo lspace="0" rspace="0">%</mo> + <mo lspace="0" rspace="0">%</mo> + </msubsup> + </math> + <math> + <munder> + <mo lspace="0" rspace="0">%</mo> + <mo lspace="0" rspace="0">%</mo> + </munder> + </math> + <math> + <mover> + <mo lspace="0" rspace="0">%</mo> + <mo lspace="0" rspace="0">%</mo> + </mover> + </math> + <math> + <munderover> + <mo lspace="0" rspace="0">%</mo> + <mo lspace="0" rspace="0">%</mo> + <mo lspace="0" rspace="0">%</mo> + </munderover> + </math> + </p> + + <p> + <math> + <mmultiscripts> + <mo lspace="0" rspace="0">%</mo> + <mo lspace="0" rspace="0">%</mo> + <mo lspace="0" rspace="0">%</mo> + <mprescripts/> + <mo lspace="0" rspace="0">%</mo> + <mo lspace="0" rspace="0">%</mo> + </mmultiscripts> + </math> + <math> + <mtable> + <mtr> + <mtd> + <mo lspace="0" rspace="0">%</mo> + </mtd> + </mtr> + </mtable> + </math> + <math> + <mo lspace="0" rspace="0">%</mo> + </math> + <math> + <msub> + <mrow> + <mo lspace="0" rspace="0">%</mo> + </mrow> + <mo lspace="0" rspace="0">%</mo> + </msub> + </math> + </p> + + <p> + <math> + <msub> + <msub> + <mo lspace="0" rspace="0">%</mo> + <mo lspace="0" rspace="0">%</mo> + </msub> + <mo lspace="0" rspace="0">%</mo> + </msub> + </math> + <math> + <munder> + <munder> + <mo lspace="0" rspace="0">%</mo> + <mo lspace="0" rspace="0">%</mo> + </munder> + <mo lspace="0" rspace="0">%</mo> + </munder> + </math> + <math> + <mfrac> + <mfrac> + <mo lspace="0" rspace="0">%</mo> + <mo lspace="0" rspace="0">%</mo> + </mfrac> + <mo lspace="0" rspace="0">%</mo> + </mfrac> + </math> + </p> + + <p> + <math> + <menclose notation="circle"> + <mo lspace="0" rspace="0">%</mo> + </menclose> + <menclose notation="circle"> + <mo lspace="0" rspace="0">%</mo> + </menclose> + <mroot> + <mo lspace="0" rspace="0">%</mo> + <mo lspace="0" rspace="0">%</mo> + </mroot> + </math> + <math> + <mpadded height="+1em"> + <mo lspace="0" rspace="0">%</mo> + </mpadded> + </math> + <math> + <merror> + <mo lspace="0" rspace="0">%</mo> + </merror> + </math> + </p> + + <math display="block"> + <mo lspace="0" rspace="0">%</mo> + </math> + + <p> + lspace and rspace rendered as appropriate + </p> + <p> + <math> + <mrow> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + </mrow> + <mfrac> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + <mo lspace="0" rspace="0">%</mo> + </mfrac> + <msqrt> + <mo lspace="0" rspace="0">%</mo> + </msqrt> + <mstyle> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + </mstyle> + </math> + </p> + + <p> + <table> + <tr> + <td> + <math> + <mphantom> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + </mphantom> + </math> + </td> + </tr> + </table> + </p> + + <p> + <math> + <mfenced> + <!-- always treated as in an mrow with > 1 child --> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + </mfenced> + </math> + <math> + <mfenced> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + </mfenced> + <menclose notation="circle"> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + </menclose> + <msub> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + <mo lspace="0" rspace="0">%</mo> + </msub> + <msup> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + <mo lspace="0" rspace="0">%</mo> + </msup> + <msubsup> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + <mo lspace="0" rspace="0">%</mo> + <mo lspace="0" rspace="0">%</mo> + </msubsup> + <munder> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + <mo lspace="0" rspace="0">%</mo> + </munder> + <mover> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + <mo lspace="0" rspace="0">%</mo> + </mover> + <munderover> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + <mo lspace="0" rspace="0">%</mo> + <mo lspace="0" rspace="0">%</mo> + </munderover> + </math> + </p> + + <p> + <math> + <mmultiscripts> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + <mo lspace="0" rspace="0">%</mo> + <mo lspace="0" rspace="0">%</mo> + <mprescripts/> + <mo lspace="0" rspace="0">%</mo> + <mo lspace="0" rspace="0">%</mo> + </mmultiscripts> + <mtable> + <mtr> + <mtd> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + </mtd> + </mtr> + </mtable> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + <msub> + <mrow> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + </mrow> + <mo lspace="0" rspace="0">%</mo> + </msub> + <msub> + <mrow> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + </mrow> + <mo lspace="0" rspace="0">%</mo> + </msub> + </math> + </p> + + <p> + <math> + <msub> + <msub> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + <mo lspace="0" rspace="0">%</mo> + </msub> + <mo lspace="0" rspace="0">%</mo> + </msub> + <munder> + <munder> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + <mo lspace="0" rspace="0">%</mo> + </munder> + <mo lspace="0" rspace="0">%</mo> + </munder> + <mfrac> + <mfrac> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + <mo lspace="0" rspace="0">%</mo> + </mfrac> + <mo lspace="0" rspace="0">%</mo> + </mfrac> + </math> + </p> + + <p> + <math> + <mrow> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + <mpadded height="+1em"> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + </mpadded> + </mrow> + </math> + <math> + <mpadded height="+1em"> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + </mpadded> + </math> + <math> + <merror> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + </merror> + </math> + </p> + + <math display="block"> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + </math> + + </body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/mathml/mo-lspace-rspace-2.html @@ -0,0 +1,377 @@ +<!DOCTYPE html> +<html> + <head> + <title>mo-lspace-rspace</title> + <style type="text/css"> + mo,td { + background-color: red; + } + msub, mfrac, msup, msubsup, mmultiscripts, + mover, munder, munderover, mpadded, merror + { + background-color: blue + } + math[display] + { + background-color: blue + } + </style> + </head> + <body> + + <p> + No lspace or rspace added: + </p> + <p> + <math> + <mrow> + <mo>%</mo> + </mrow> + </math> + <math> + <mfrac> + <mo>%</mo> + <mo>%</mo> + </mfrac> + </math> + <math> + <msqrt> + <mo>%</mo> + </msqrt> + </math> + <math> + <mroot> + <mo>%</mo> + <mo>%</mo> + </mroot> + </math> + <math> + <mstyle> + <mo>%</mo> + </mstyle> + </math> + </p> + + <p> + <table> + <tr> + <td> + <math> + <mphantom> + <mo>%</mo> + </mphantom> + </math> + </td> + </tr> + </table> + </p> + + <p> + <math> + <menclose notation="circle"> + <mo>%</mo> + </menclose> + </math> + <math> + <msub> + <mo>%</mo> + <mo>%</mo> + </msub> + </math> + <math> + <msup> + <mo>%</mo> + <mo>%</mo> + </msup> + </math> + <math> + <msubsup> + <mo>%</mo> + <mo>%</mo> + <mo>%</mo> + </msubsup> + </math> + <math> + <munder> + <mo>%</mo> + <mo>%</mo> + </munder> + </math> + <math> + <mover> + <mo>%</mo> + <mo>%</mo> + </mover> + </math> + <math> + <munderover> + <mo>%</mo> + <mo>%</mo> + <mo>%</mo> + </munderover> + </math> + </p> + + <p> + <math> + <mmultiscripts> + <mo>%</mo> + <mo>%</mo> + <mo>%</mo> + <mprescripts/> + <mo>%</mo> + <mo>%</mo> + </mmultiscripts> + </math> + <math> + <mtable> + <mtr> + <mtd> + <mo>%</mo> + </mtd> + </mtr> + </mtable> + </math> + <math> + <mo>%</mo> + </math> + <math> + <msub> + <mrow> + <mo>%</mo> + </mrow> + <mo>%</mo> + </msub> + </math> + </p> + + <p> + <math> + <msub> + <msub> + <mo>%</mo> + <mo>%</mo> + </msub> + <mo>%</mo> + </msub> + </math> + <math> + <munder> + <munder> + <mo>%</mo> + <mo>%</mo> + </munder> + <mo>%</mo> + </munder> + </math> + <math> + <mfrac> + <mfrac> + <mo>%</mo> + <mo>%</mo> + </mfrac> + <mo>%</mo> + </mfrac> + </math> + </p> + <p> + <math> + <menclose notation="circle"> + <mo>%</mo> + </menclose> + <menclose notation="circle"> + <mo>%</mo> + </menclose> + <mroot> + <mo>%</mo> + <mo>%</mo> + </mroot> + </math> + <math> + <mpadded height="+1em"> + <mo>%</mo> + </mpadded> + </math> + <math> + <merror> + <mo>%</mo> + </merror> + </math> + </p> + + <math display="block"> + <mo>%</mo> + </math> + + <p> + lspace and rspace rendered as appropriate + </p> + <p> + <math> + <mrow> + <mo>%</mo> + </mrow> + <mfrac> + <mo>%</mo> + <mo>%</mo> + </mfrac> + <msqrt> + <mo>%</mo> + </msqrt> + <mstyle> + <mo>%</mo> + </mstyle> + </math> + </p> + + <p> + <table> + <tr> + <td> + <math> + <mphantom> + <mo>%</mo> + <mo>%</mo> + </mphantom> + </math> + </td> + </tr> + </table> + </p> + + <p> + <math> + <mfenced> + <mo>%</mo> + </mfenced> + </math> + <math> + <mfenced> + <mo>%</mo> + <mo>%</mo> + </mfenced> + <menclose notation="circle"> + <mo>%</mo> + <mo>%</mo> + </menclose> + <msub> + <mo>%</mo> + <mo>%</mo> + </msub> + <msup> + <mo>%</mo> + <mo>%</mo> + </msup> + <msubsup> + <mo>%</mo> + <mo>%</mo> + <mo>%</mo> + </msubsup> + <munder> + <mo>%</mo> + <mo>%</mo> + </munder> + <mover> + <mo>%</mo> + <mo>%</mo> + </mover> + <munderover> + <mo>%</mo> + <mo>%</mo> + <mo>%</mo> + </munderover> + </math> + </p> + + <p> + <math> + <mmultiscripts> + <mo>%</mo> + <mo>%</mo> + <mo>%</mo> + <mprescripts/> + <mo>%</mo> + <mo>%</mo> + </mmultiscripts> + <mtable> + <mtr> + <mtd> + <mo>%</mo> + <mo>%</mo> + </mtd> + </mtr> + </mtable> + <mo>%</mo> + <msub> + <mrow> + <mo>%</mo> + </mrow> + <mo>%</mo> + </msub> + <msub> + <mrow> + <mo>%</mo> + <mo>%</mo> + </mrow> + <mo>%</mo> + </msub> + </math> + </p> + + <p> + <math> + <msub> + <msub> + <mo>%</mo> + <mo>%</mo> + </msub> + <mo>%</mo> + </msub> + <munder> + <munder> + <mo>%</mo> + <mo>%</mo> + </munder> + <mo>%</mo> + </munder> + <mfrac> + <mfrac> + <mo>%</mo> + <mo>%</mo> + </mfrac> + <mo>%</mo> + </mfrac> + </math> + </p> + + <p> + <math> + <mrow> + <mo>%</mo> + <mo>%</mo> + <mpadded height="+1em"> + <mo>%</mo> + </mpadded> + </mrow> + </math> + <math> + <mpadded height="+1em"> + <mo>%</mo> + <mo>%</mo> + </mpadded> + </math> + <math> + <merror> + <mo>%</mo> + <mo>%</mo> + </merror> + </math> + </p> + + <math display="block"> + <mo>%</mo> + <mo>%</mo> + </math> + + </body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/mathml/mo-lspace-rspace-3-ref.html @@ -0,0 +1,37 @@ +<html> + <body> + <p> + <math> + <mo id="mo1" LSPACE="3em" rspace="0em">%</mo> + </math> + </p> + <p> + <math> + <mo id="mo2" lspace="0em" rspace="0em">%</mo> + </math> + </p> + <p> + <math> + <mo id="mo3" lspace="3em" rspace="thinmathspace">%</mo> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + </math> + </p> + <p> + <math> + <mo id="mo4" lspace="thinmathspace" rspace="thinmathspace">%</mo> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + </math> + </p> + <p> + <math> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + <mo lspace="thinmathspace" rspace="thinmathspace">%</mo> + </math> + </p> + <p> + <math> + <mo lspace="0em" rspace="0em">%</mo> + </math> + </p> + </body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/mathml/mo-lspace-rspace-3.html @@ -0,0 +1,56 @@ +<html class="reftest-wait"> + <body> + <p> + <math> + <mo id="mo1">%</mo> + </math> + </p> + <p> + <math> + <mo id="mo2" lspace="3em">%</mo> + </math> + </p> + <p> + <math> + <mo id="mo3">%</mo> + <mo>%</mo> + </math> + </p> + <p> + <math> + <mo id="mo4" lspace="3em">%</mo> + <mo >%</mo> + </math> + </p> + <p> + <math id="math1"> + <mo>%</mo> + </math> + </p> + <p> + <math id="math2"> + <mo>%</mo> + <mo id="mo5">%</mo> + </math> + </p> + <script type="text/javascript"> + function doTest() { + // Add and remove lspace + document.getElementById("mo1").setAttribute("lspace", "3em"); + document.getElementById("mo2").removeAttribute("lspace"); + // and again but with an inferred mrow + document.getElementById("mo3").setAttribute("lspace", "3em"); + document.getElementById("mo4").removeAttribute("lspace"); + + // Change to/from inferred mrow + var mo1 = document.createElementNS("http://www.w3.org/1998/Math/MathML", "mo"); + mo1.innerHTML = "%"; + document.getElementById("math1").appendChild(mo1); + document.getElementById("math2").removeChild(document.getElementById("mo5")); + + document.documentElement.removeAttribute("class"); + } + window.addEventListener("MozReftestInvalidate", doTest, false); + </script> + </body> +</html>
--- a/layout/reftests/mathml/op-dict-1-ref.html +++ b/layout/reftests/mathml/op-dict-1-ref.html @@ -2,12 +2,14 @@ <html> <head> <title>op-dict mo form</title> </head> <body> <math> <mrow> <mo form="prefix">+</mo> + <!-- need a second child to avoid zeroing dictionary spacing --> + <mn>1</mn> </mrow> </math> </body> </html>
--- a/layout/reftests/mathml/op-dict-1.html +++ b/layout/reftests/mathml/op-dict-1.html @@ -2,12 +2,13 @@ <html> <head> <title>op-dict mo form</title> </head> <body> <math> <mrow> <mo form="infix">+</mo> + <mn>1</mn> </mrow> </math> </body> </html>
--- a/layout/reftests/mathml/reftest.list +++ b/layout/reftests/mathml/reftest.list @@ -136,16 +136,18 @@ fails-if(B2G) == mpadded-9.html mpadded- == mtable-columnalign-multi-mtr-dynamic.html mtable-columnalign-multi-ref.html == mtable-columnalign-multi-mtable.html mtable-columnalign-multi-ref.html == mtable-columnalign-multi-mtable-dynamic.html mtable-columnalign-multi-ref.html == maction-selection.html maction-selection-ref.html == maction-dynamic-embellished-op.html maction-dynamic-embellished-op-ref.html skip-if(B2G) == maction-dynamic-1.html maction-dynamic-1-ref.html # bug 773482 == maction-dynamic-2.html maction-dynamic-2-ref.html == mo-lspace-rspace.html mo-lspace-rspace-ref.html +== mo-lspace-rspace-2.html mo-lspace-rspace-2-ref.html +== mo-lspace-rspace-3.html mo-lspace-rspace-3-ref.html == mo-invisibleoperators.html mo-invisibleoperators-ref.html == mo-invisibleoperators-2.html mo-invisibleoperators-2-ref.html skip-if(B2G) == maction-dynamic-3.html maction-dynamic-3-ref.html # bug 773482 == whitespace-trim-1.html whitespace-trim-1-ref.html == whitespace-trim-2.html whitespace-trim-2-ref.html == whitespace-trim-3.html whitespace-trim-3-ref.html fails == whitespace-trim-4.html whitespace-trim-4-ref.html # Bug 787215 == whitespace-trim-5.html whitespace-trim-5-ref.html
--- a/layout/xul/nsMenuBarFrame.cpp +++ b/layout/xul/nsMenuBarFrame.cpp @@ -233,17 +233,17 @@ nsMenuBarFrame::FindMenuWithShortcut(nsI if (soundInterface) soundInterface->Beep(); } nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); if (pm) { nsIFrame* popup = pm->GetTopPopup(ePopupTypeAny); if (popup) - pm->HidePopup(popup->GetContent(), true, true, true); + pm->HidePopup(popup->GetContent(), true, true, true, false); } SetCurrentMenuItem(nullptr); SetActive(false); #endif // #ifdef XP_WIN return nullptr; @@ -303,17 +303,17 @@ public: if (mOldMenu && mNewMenu) { menubar = do_QueryFrame(mMenuBar->GetPrimaryFrame()); if (menubar) menubar->SetStayActive(true); } if (mOldMenu) { nsWeakFrame weakMenuBar(menubar); - pm->HidePopup(mOldMenu, false, false, false); + pm->HidePopup(mOldMenu, false, false, false, false); // clear the flag again if (mNewMenu && weakMenuBar.IsAlive()) menubar->SetStayActive(false); } if (mNewMenu) pm->ShowMenu(mNewMenu, mSelectFirstItem, false);
--- a/layout/xul/nsMenuBarListener.cpp +++ b/layout/xul/nsMenuBarListener.cpp @@ -101,17 +101,17 @@ void nsMenuBarListener::InitAccessKey() void nsMenuBarListener::ToggleMenuActiveState() { nsMenuFrame* closemenu = mMenuBarFrame->ToggleMenuActiveState(); nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); if (pm && closemenu) { nsMenuPopupFrame* popupFrame = closemenu->GetPopup(); if (popupFrame) - pm->HidePopup(popupFrame->GetContent(), false, false, true); + pm->HidePopup(popupFrame->GetContent(), false, false, true, false); } } //////////////////////////////////////////////////////////////////////// nsresult nsMenuBarListener::KeyUp(nsIDOMEvent* aKeyEvent) { nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aKeyEvent);
--- a/layout/xul/nsMenuBoxObject.cpp +++ b/layout/xul/nsMenuBoxObject.cpp @@ -44,17 +44,17 @@ NS_IMETHODIMP nsMenuBoxObject::OpenMenu( nsCOMPtr<nsIContent> content = mContent; pm->ShowMenu(content, false, false); } else { nsMenuFrame* menu = do_QueryFrame(frame); if (menu) { nsMenuPopupFrame* popupFrame = menu->GetPopup(); if (popupFrame) - pm->HidePopup(popupFrame->GetContent(), false, true, false); + pm->HidePopup(popupFrame->GetContent(), false, true, false, false); } } } } return NS_OK; }
--- a/layout/xul/nsMenuFrame.cpp +++ b/layout/xul/nsMenuFrame.cpp @@ -708,17 +708,17 @@ nsMenuFrame::OpenMenu(bool aSelectFirstI void nsMenuFrame::CloseMenu(bool aDeselectMenu) { gEatMouseMove = true; // Close the menu asynchronously nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); if (pm && HasPopup()) - pm->HidePopup(GetPopup()->GetContent(), false, aDeselectMenu, true); + pm->HidePopup(GetPopup()->GetContent(), false, aDeselectMenu, true, false); } bool nsMenuFrame::IsSizedToPopup(nsIContent* aContent, bool aRequireAlways) { nsAutoString sizedToPopup; aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::sizetopopup, sizedToPopup); return sizedToPopup.EqualsLiteral("always") || @@ -802,17 +802,17 @@ nsMenuFrame::Enter(WidgetGUIEvent* aEven if (IsDisabled()) { #ifdef XP_WIN // behavior on Windows - close the popup chain if (mMenuParent) { nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); if (pm) { nsIFrame* popup = pm->GetTopPopup(ePopupTypeAny); if (popup) - pm->HidePopup(popup->GetContent(), true, true, true); + pm->HidePopup(popup->GetContent(), true, true, true, false); } } #endif // #ifdef XP_WIN // this menu item was disabled - exit return nullptr; } if (!IsOpen()) {
--- a/layout/xul/nsPopupBoxObject.cpp +++ b/layout/xul/nsPopupBoxObject.cpp @@ -45,17 +45,17 @@ nsPopupBoxObject::GetPopupSetFrame() return rootBox->GetPopupSetFrame(); } NS_IMETHODIMP nsPopupBoxObject::HidePopup() { nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); if (pm && mContent) - pm->HidePopup(mContent, false, true, false); + pm->HidePopup(mContent, false, true, false, false); return NS_OK; } NS_IMETHODIMP nsPopupBoxObject::ShowPopup(nsIDOMElement* aAnchorElement, nsIDOMElement* aPopupElement, int32_t aXPos, int32_t aYPos,
--- a/layout/xul/nsResizerFrame.cpp +++ b/layout/xul/nsResizerFrame.cpp @@ -248,34 +248,37 @@ nsResizerFrame::HandleEvent(nsPresContex nsWeakFrame weakFrame(menuPopupFrame); if (menuPopupFrame) { nsCOMPtr<nsIWidget> widget = menuPopupFrame->GetWidget(); if (widget) widget->GetScreenBounds(oldRect); // convert the new rectangle into outer window coordinates nsIntPoint clientOffset = widget->GetClientOffset(); - rect.x -= clientOffset.x; - rect.y -= clientOffset.y; + rect.x -= clientOffset.x; + rect.y -= clientOffset.y; } SizeInfo sizeInfo, originalSizeInfo; sizeInfo.width.AppendInt(cssRect.width); sizeInfo.height.AppendInt(cssRect.height); ResizeContent(contentToResize, direction, sizeInfo, &originalSizeInfo); MaybePersistOriginalSize(contentToResize, originalSizeInfo); // Move the popup to the new location unless it is anchored, since // the position shouldn't change. nsMenuPopupFrame::SetPopupPosition // will instead ensure that the popup's position is anchored at the // right place. if (weakFrame.IsAlive() && (oldRect.x != rect.x || oldRect.y != rect.y) && (!menuPopupFrame->IsAnchored() || menuPopupFrame->PopupLevel() != ePopupLevelParent)) { + + rect.x = aPresContext->DevPixelsToIntCSSPixels(rect.x); + rect.y = aPresContext->DevPixelsToIntCSSPixels(rect.y); menuPopupFrame->MoveTo(rect.x, rect.y, true); } } else { window->SetPositionAndSize(rect.x, rect.y, rect.width, rect.height, true); // do the repaint. } doDefault = false;
--- a/layout/xul/nsTitleBarFrame.cpp +++ b/layout/xul/nsTitleBarFrame.cpp @@ -122,17 +122,20 @@ nsTitleBarFrame::HandleEvent(nsPresConte // if the titlebar is in a popup, move the popup frame, otherwise // move the widget associated with the window if (parent) { nsMenuPopupFrame* menuPopupFrame = static_cast<nsMenuPopupFrame*>(parent); nsCOMPtr<nsIWidget> widget = menuPopupFrame->GetWidget(); nsIntRect bounds; widget->GetScreenBounds(bounds); - menuPopupFrame->MoveTo(bounds.x + nsMoveBy.x, bounds.y + nsMoveBy.y, false); + + int32_t newx = aPresContext->DevPixelsToIntCSSPixels(bounds.x + nsMoveBy.x); + int32_t newy = aPresContext->DevPixelsToIntCSSPixels(bounds.y + nsMoveBy.y); + menuPopupFrame->MoveTo(newx, newy, false); } else { nsIPresShell* presShell = aPresContext->PresShell(); nsPIDOMWindow *window = presShell->GetDocument()->GetWindow(); if (window) { window->MoveBy(nsMoveBy.x, nsMoveBy.y); } }
--- a/layout/xul/nsXULPopupManager.cpp +++ b/layout/xul/nsXULPopupManager.cpp @@ -225,17 +225,17 @@ nsXULPopupManager::Rollup(uint32_t aCoun while (--aCount && last->GetParent()) { last = last->GetParent(); } if (last) { lastPopup = last->Content(); } } - HidePopup(item->Content(), true, true, false, lastPopup); + HidePopup(item->Content(), true, true, false, true, lastPopup); } return consume; } //////////////////////////////////////////////////////////////////////// bool nsXULPopupManager::ShouldRollupOnMouseWheelEvent() { @@ -821,16 +821,17 @@ nsXULPopupManager::ShowPopupCallback(nsI CheckCaretDrawingState(); } void nsXULPopupManager::HidePopup(nsIContent* aPopup, bool aHideChain, bool aDeselectMenu, bool aAsynchronous, + bool aIsRollup, nsIContent* aLastPopup) { // if the popup is on the nohide panels list, remove it but don't close any // other panels nsMenuPopupFrame* popupFrame = nullptr; bool foundPanel = false; nsMenuChainItem* item = mNoHidePanels; while (item) { @@ -913,26 +914,72 @@ nsXULPopupManager::HidePopup(nsIContent* // run again. In the invisible state, we just want the events to fire. if (state != ePopupInvisible) popupFrame->SetPopupState(ePopupHiding); // for menus, popupToHide is always the frontmost item in the list to hide. if (aAsynchronous) { nsCOMPtr<nsIRunnable> event = new nsXULPopupHidingEvent(popupToHide, nextPopup, lastPopup, - type, deselectMenu); + type, deselectMenu, aIsRollup); NS_DispatchToCurrentThread(event); } else { FirePopupHidingEvent(popupToHide, nextPopup, lastPopup, - popupFrame->PresContext(), type, deselectMenu); + popupFrame->PresContext(), type, deselectMenu, aIsRollup); } } } +// This is used to hide the popup after a transition finishes. +class TransitionEnder : public nsIDOMEventListener +{ +public: + + nsCOMPtr<nsIContent> mContent; + bool mDeselectMenu; + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS(TransitionEnder) + + TransitionEnder(nsIContent* aContent, bool aDeselectMenu) + : mContent(aContent), mDeselectMenu(aDeselectMenu) + { + } + + virtual ~TransitionEnder() { } + + NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) MOZ_OVERRIDE + { + mContent->RemoveSystemEventListener(NS_LITERAL_STRING("transitionend"), this, false); + + nsMenuPopupFrame* popupFrame = do_QueryFrame(mContent->GetPrimaryFrame()); + + // Now hide the popup. There could be other properties transitioning, but + // we'll assume they all end at the same time and just hide the popup upon + // the first one ending. + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm && popupFrame) { + pm->HidePopupCallback(mContent, popupFrame, nullptr, nullptr, + popupFrame->PopupType(), mDeselectMenu); + } + + return NS_OK; + } +}; + +NS_IMPL_CYCLE_COLLECTING_ADDREF(TransitionEnder) +NS_IMPL_CYCLE_COLLECTING_RELEASE(TransitionEnder) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TransitionEnder) + NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTION_1(TransitionEnder, mContent); + void nsXULPopupManager::HidePopupCallback(nsIContent* aPopup, nsMenuPopupFrame* aPopupFrame, nsIContent* aNextPopup, nsIContent* aLastPopup, nsPopupType aPopupType, bool aDeselectMenu) { @@ -1013,27 +1060,27 @@ nsXULPopupManager::HidePopupCallback(nsI nsPopupState state = popupFrame->PopupState(); if (state == ePopupHiding) return; if (state != ePopupInvisible) popupFrame->SetPopupState(ePopupHiding); FirePopupHidingEvent(popupToHide, nextPopup, aLastPopup, popupFrame->PresContext(), - foundMenu->PopupType(), aDeselectMenu); + foundMenu->PopupType(), aDeselectMenu, false); } } } void nsXULPopupManager::HidePopup(nsIFrame* aFrame) { nsMenuPopupFrame* popup = do_QueryFrame(aFrame); if (popup) - HidePopup(aFrame->GetContent(), false, true, false); + HidePopup(aFrame->GetContent(), false, true, false, false); } void nsXULPopupManager::HidePopupAfterDelay(nsMenuPopupFrame* aPopup) { // Don't close up immediately. // Kick off a close timer. KillMenuTimer(); @@ -1282,17 +1329,18 @@ nsXULPopupManager::FirePopupShowingEvent } void nsXULPopupManager::FirePopupHidingEvent(nsIContent* aPopup, nsIContent* aNextPopup, nsIContent* aLastPopup, nsPresContext *aPresContext, nsPopupType aPopupType, - bool aDeselectMenu) + bool aDeselectMenu, + bool aIsRollup) { nsCOMPtr<nsIPresShell> presShell = aPresContext->PresShell(); nsEventStatus status = nsEventStatus_eIgnore; WidgetMouseEvent event(true, NS_XUL_POPUP_HIDING, nullptr, WidgetMouseEvent::eReal); EventDispatcher::Dispatch(aPopup, aPresContext, &event, nullptr, &status); @@ -1321,16 +1369,40 @@ nsXULPopupManager::FirePopupHidingEvent( // if the event was cancelled, don't hide the popup, and reset its // state back to open. Only popups in chrome shells can prevent a popup // from hiding. if (status == nsEventStatus_eConsumeNoDefault && !popupFrame->IsInContentShell()) { popupFrame->SetPopupState(ePopupOpenAndVisible); } else { + // If the popup has an animate attribute and it is not set to false, assume + // that it has a closing transition and wait for it to finish. The transition + // may still occur either way, but the view will be hidden and you won't be + // able to see it. If there is a next popup, indicating that mutliple popups + // are rolling up, don't wait and hide the popup right away since the effect + // would likely be undesirable. This also does a quick check to see if the + // popup has a transition defined, and skips the wait if not. + if (!aNextPopup && aPopup->HasAttr(kNameSpaceID_None, nsGkAtoms::animate) && + popupFrame->StyleDisplay()->mTransitionPropertyCount > 0) { + nsAutoString animate; + aPopup->GetAttr(kNameSpaceID_None, nsGkAtoms::animate, animate); + + // If animate="false" then don't transition at all. If animate="cancel", + // only show the transition if cancelling the popup or rolling up. + // Otherwise, always show the transition. + if (!animate.EqualsLiteral("false") && + (!animate.EqualsLiteral("cancel") || aIsRollup)) { + nsCOMPtr<TransitionEnder> ender = new TransitionEnder(aPopup, aDeselectMenu); + aPopup->AddSystemEventListener(NS_LITERAL_STRING("transitionend"), + ender, false, false); + return; + } + } + HidePopupCallback(aPopup, popupFrame, aNextPopup, aLastPopup, aPopupType, aDeselectMenu); } } } bool nsXULPopupManager::IsPopupOpen(nsIContent* aPopup) @@ -1575,17 +1647,17 @@ nsXULPopupManager::PopupDestroyed(nsMenu // it asynchronously since we are in the middle of frame destruction. nsMenuPopupFrame* childframe = child->Frame(); if (nsLayoutUtils::IsProperAncestorFrame(frame, childframe)) { popupsToHide.AppendElement(childframe); } else { // HidePopup will take care of hiding any of its children, so // break out afterwards - HidePopup(child->Content(), false, false, true); + HidePopup(child->Content(), false, false, true, false); break; } child = child->GetChild(); } } item->Detach(&mPopups); @@ -1775,17 +1847,17 @@ nsXULPopupManager::Notify(nsITimer* aTim void nsXULPopupManager::KillMenuTimer() { if (mCloseTimer && mTimerMenu) { mCloseTimer->Cancel(); mCloseTimer = nullptr; if (mTimerMenu->IsOpen()) - HidePopup(mTimerMenu->GetContent(), false, false, true); + HidePopup(mTimerMenu->GetContent(), false, false, true, false); } mTimerMenu = nullptr; } void nsXULPopupManager::CancelMenuTimer(nsMenuParent* aMenuParent) { @@ -1971,17 +2043,17 @@ nsXULPopupManager::HandleKeyboardNavigat return true; } } else if (currentMenu && isContainer && isOpen) { if (aDir == eNavigationDirection_Start) { // close a submenu when Left is pressed nsMenuPopupFrame* popupFrame = currentMenu->GetPopup(); if (popupFrame) - HidePopup(popupFrame->GetContent(), false, false, false); + HidePopup(popupFrame->GetContent(), false, false, false, false); return true; } } return false; } bool @@ -1991,17 +2063,17 @@ nsXULPopupManager::HandleKeyboardEventWi { uint32_t keyCode; aKeyEvent->GetKeyCode(&keyCode); // Escape should close panels, but the other keys should have no effect. if (aTopVisibleMenuItem && aTopVisibleMenuItem->PopupType() != ePopupTypeMenu) { if (keyCode == nsIDOMKeyEvent::DOM_VK_ESCAPE) { - HidePopup(aTopVisibleMenuItem->Content(), false, false, false); + HidePopup(aTopVisibleMenuItem->Content(), false, false, false, true); aKeyEvent->StopPropagation(); aKeyEvent->PreventDefault(); } return true; } bool consume = (mPopups || mActiveMenuBar); switch (keyCode) { @@ -2015,17 +2087,17 @@ nsXULPopupManager::HandleKeyboardEventWi break; case nsIDOMKeyEvent::DOM_VK_ESCAPE: // Pressing Escape hides one level of menus only. If no menu is open, // check if a menubar is active and inform it that a menu closed. Even // though in this latter case, a menu didn't actually close, the effect // ends up being the same. Similar for the tab key below. if (aTopVisibleMenuItem) { - HidePopup(aTopVisibleMenuItem->Content(), false, false, false); + HidePopup(aTopVisibleMenuItem->Content(), false, false, false, true); } else if (mActiveMenuBar) { mActiveMenuBar->MenuClosed(); } break; case nsIDOMKeyEvent::DOM_VK_TAB: #ifndef XP_MACOSX case nsIDOMKeyEvent::DOM_VK_F10: @@ -2322,17 +2394,17 @@ nsXULPopupHidingEvent::Run() nsIDocument *document = mPopup->GetCurrentDoc(); if (pm && document) { nsIPresShell* presShell = document->GetShell(); if (presShell) { nsPresContext* context = presShell->GetPresContext(); if (context) { pm->FirePopupHidingEvent(mPopup, mNextPopup, mLastPopup, - context, mPopupType, mDeselectMenu); + context, mPopupType, mDeselectMenu, mIsRollup); } } } return NS_OK; } NS_IMETHODIMP @@ -2383,12 +2455,12 @@ nsXULMenuCommandEvent::Run() AutoHandlingUserInputStatePusher userInpStatePusher(mUserInput, nullptr, shell->GetDocument()); nsContentUtils::DispatchXULCommand(mMenu, mIsTrusted, nullptr, shell, mControl, mAlt, mShift, mMeta); } if (popup && mCloseMenuMode != CloseMenuMode_None) - pm->HidePopup(popup, mCloseMenuMode == CloseMenuMode_Auto, true, false); + pm->HidePopup(popup, mCloseMenuMode == CloseMenuMode_Auto, true, false, false); return NS_OK; }
--- a/layout/xul/nsXULPopupManager.h +++ b/layout/xul/nsXULPopupManager.h @@ -199,35 +199,38 @@ private: // this class is used for dispatching popuphiding events asynchronously. class nsXULPopupHidingEvent : public nsRunnable { public: nsXULPopupHidingEvent(nsIContent *aPopup, nsIContent* aNextPopup, nsIContent* aLastPopup, nsPopupType aPopupType, - bool aDeselectMenu) + bool aDeselectMenu, + bool aIsRollup) : mPopup(aPopup), mNextPopup(aNextPopup), mLastPopup(aLastPopup), mPopupType(aPopupType), - mDeselectMenu(aDeselectMenu) + mDeselectMenu(aDeselectMenu), + mIsRollup(aIsRollup) { NS_ASSERTION(aPopup, "null popup supplied to nsXULPopupHidingEvent constructor"); // aNextPopup and aLastPopup may be null } NS_IMETHOD Run() MOZ_OVERRIDE; private: nsCOMPtr<nsIContent> mPopup; nsCOMPtr<nsIContent> mNextPopup; nsCOMPtr<nsIContent> mLastPopup; nsPopupType mPopupType; bool mDeselectMenu; + bool mIsRollup; }; // this class is used for dispatching menu command events asynchronously. class nsXULMenuCommandEvent : public nsRunnable { public: nsXULMenuCommandEvent(nsIContent *aMenu, bool aIsTrusted, @@ -271,16 +274,17 @@ class nsXULPopupManager MOZ_FINAL : publ public nsITimerCallback, public nsIObserver { public: friend class nsXULPopupShowingEvent; friend class nsXULPopupHidingEvent; friend class nsXULMenuCommandEvent; + friend class TransitionEnder; NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER NS_DECL_NSITIMERCALLBACK NS_DECL_NSIDOMEVENTLISTENER // nsIRollupListener virtual bool Rollup(uint32_t aCount, const nsIntPoint* pos, nsIContent** aLastRolledUp) MOZ_OVERRIDE; @@ -428,23 +432,25 @@ public: * aHideChain - true if the entire chain of menus should be closed. If false, * only this popup is closed. * aDeselectMenu - true if the parent <menu> of the popup should be deselected. * This will be false when the menu is closed by pressing the * Escape key. * aAsynchronous - true if the first popuphiding event should be sent * asynchrously. This should be true if HidePopup is called * from a frame. + * aIsRollup - true if this popup is hiding due to a rollup or escape keypress. * aLastPopup - optional popup to close last when hiding a chain of menus. * If null, then all popups will be closed. */ void HidePopup(nsIContent* aPopup, bool aHideChain, bool aDeselectMenu, bool aAsynchronous, + bool aIsRollup, nsIContent* aLastPopup = nullptr); /** * Hide the popup aFrame. This method is called by the view manager when the * close button is pressed. */ void HidePopup(nsIFrame* aFrame); @@ -659,23 +665,25 @@ protected: * The caller must keep a strong reference to aPopup, aNextPopup and aLastPopup. * * aPopup - the popup to hide * aNextPopup - the next popup to hide * aLastPopup - the last popup in the chain to hide * aPresContext - nsPresContext for the popup's frame * aPopupType - the PopupType of the frame. * aDeselectMenu - true to unhighlight the menu when hiding it + * aIsRollup - true if this popup is hiding due to a rollup or escape keypress */ void FirePopupHidingEvent(nsIContent* aPopup, nsIContent* aNextPopup, nsIContent* aLastPopup, nsPresContext *aPresContext, nsPopupType aPopupType, - bool aDeselectMenu); + bool aDeselectMenu, + bool aIsRollup); /** * Handle keyboard navigation within a menu popup specified by aItem. */ bool HandleKeyboardNavigationInPopup(nsMenuChainItem* aItem, nsNavigationDirection aDir) { return HandleKeyboardNavigationInPopup(aItem, aItem->Frame(), aDir);
--- a/layout/xul/nsXULTooltipListener.cpp +++ b/layout/xul/nsXULTooltipListener.cpp @@ -517,17 +517,17 @@ nsXULTooltipListener::LaunchTooltip() nsresult nsXULTooltipListener::HideTooltip() { #ifdef MOZ_XUL nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip); if (currentTooltip) { nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); if (pm) - pm->HidePopup(currentTooltip, false, false, false); + pm->HidePopup(currentTooltip, false, false, false, false); } #endif DestroyTooltip(); return NS_OK; } static void
--- a/media/mtransport/nricectx.cpp +++ b/media/mtransport/nricectx.cpp @@ -129,16 +129,18 @@ static int nr_crypto_nss_hmac(UCHAR *key skey = PK11_ImportSymKey(slot, mech, PK11_OriginUnwrap, CKA_SIGN, &keyi, nullptr); if (!skey) goto abort; hmac_ctx = PK11_CreateContextBySymKey(mech, CKA_SIGN, skey, ¶m); + if (!hmac_ctx) + goto abort; status = PK11_DigestBegin(hmac_ctx); if (status != SECSuccess) goto abort; status = PK11_DigestOp(hmac_ctx, buf, bufl); if (status != SECSuccess) goto abort;
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h @@ -43,16 +43,17 @@ namespace test { class AFakePCObserver; #endif } #ifdef USE_FAKE_MEDIA_STREAMS class Fake_DOMMediaStream; #endif +class nsGlobalWindow; class nsIDOMMediaStream; class nsDOMDataChannel; namespace mozilla { class DataChannel; class DtlsIdentity; class NrIceCtx; class NrIceMediaStream;
--- a/modules/libmar/moz.build +++ b/modules/libmar/moz.build @@ -7,12 +7,15 @@ DIRS += ['src'] if CONFIG['MOZ_ENABLE_SIGNMAR']: DIRS += ['sign', 'verify'] TEST_DIRS += ['tests'] elif CONFIG['OS_ARCH'] == 'WINNT': # On Windows we don't verify with NSS and updater needs to link to it DIRS += ['verify'] +elif CONFIG['OS_ARCH'] == 'Darwin': + # On OSX 10.7+ we don't verify with NSS and updater needs to link to it + DIRS += ['verify'] # If we are building ./sign and ./verify then ./tool must come after it DIRS += ['tool']
--- a/modules/libmar/tests/unit/test_sign_verify.js +++ b/modules/libmar/tests/unit/test_sign_verify.js @@ -137,46 +137,48 @@ function run_test() { // Make sure the signmar binary exists and is an executable. do_check_true(signmarBin.exists()); do_check_true(signmarBin.isExecutable()); // Will reference the arguments to use for verification in signmar let args = []; - // The XPCShell test wiki indicates this is the preferred way for - // Windows detection. + // The XPCShell test wiki indicates this is the preferred way for + // Windows and OSX detection. var isWindows = ("@mozilla.org/windows-registry-key;1" in Cc); + var isOSX = ("nsILocalFileMac" in Components.interfaces); // Setup the command line arguments to create the MAR. - // Windows vs. Linux/Mac/... have different command line for verification - // since on Windows we verify with CryptoAPI and on all other platforms - // we verify with NSS. So on Windows we use an exported DER file and on - // other platforms we use the NSS config db. - if (isWindows) { - if (certs.length == 1 && useShortHandCmdLine) { - args.push("-D", "data/" + certs[0] + ".der"); - } else { - for (i = 0; i < certs.length; i++) { - args.push("-D" + i, "data/" + certs[i] + ".der"); - } - } - args.push("-v", signedMAR.path); - } else { + // Windows & Mac vs. Linux/... have different command line for verification + // since on Windows we verify with CryptoAPI, on Mac with Security + // Transforms or NSS and on all other platforms we verify with NSS. So on + // Windows and Mac we use an exported DER file and on other platforms we use + // the NSS config db. + if (!isWindows) { let NSSConfigDir = do_get_file("data"); args = ["-d", NSSConfigDir.path]; if (certs.length == 1 && useShortHandCmdLine) { args.push("-n", certs[0]); } else { - for (i = 0; i < certs.length; i++) { + for (var i = 0; i < certs.length; i++) { args.push("-n" + i, certs[i]); } } - args.push("-v", signedMAR.path); } + if (isWindows || isOSX) { + if (certs.length == 1 && useShortHandCmdLine) { + args.push("-D", "data/" + certs[0] + ".der"); + } else { + for (var i = 0; i < certs.length; i++) { + args.push("-D" + i, "data/" + certs[i] + ".der"); + } + } + } + args.push("-v", signedMAR.path); process.init(signmarBin); try { // We put this in a try block because nsIProcess doesn't like -1 returns process.run(true, args, args.length); } catch (e) { // On Windows negative return value throws an exception process.exitValue = -1;
--- a/modules/libmar/tool/mar.c +++ b/modules/libmar/tool/mar.c @@ -53,20 +53,28 @@ static void print_usage() { "signed_input_archive.mar base_64_encoded_signature_file " "changed_signed_output.mar\n"); printf("(i) is the index of the certificate to extract\n"); #if defined(XP_WIN) && !defined(MAR_NSS) printf("Verify a MAR file:\n"); printf(" mar [-C workingDir] -D DERFilePath -v signed_archive.mar\n"); printf("At most %d signature certificate DER files are specified by " "-D0 DERFilePath1 -D1 DERFilePath2, ...\n", MAX_SIGNATURES); -#else +#elif defined(XP_MACOSX) printf("Verify a MAR file:\n"); printf(" mar [-C workingDir] -d NSSConfigDir -n certname " - "-v signed_archive.mar\n"); + "-v signed_archive.mar -D DERFilePath\n"); + printf("At most %d signature certificate names are specified by " + "-n0 certName -n1 certName2, ...\n", MAX_SIGNATURES); + printf("At most %d signature certificate DER files are specified by " + "-D0 DERFilePath1 -D1 DERFilePath2, ...\n", MAX_SIGNATURES); +#else + printf("Verify a MAR file:\n"); + printf(" mar [-C workingDir] -d NSSConfigDir -n certname " + "-v signed_archive.mar\n"); printf("At most %d signature certificate names are specified by " "-n0 certName -n1 certName2, ...\n", MAX_SIGNATURES); #endif printf("At most %d verification certificate names are specified by " "-n0 certName -n1 certName2, ...\n", MAX_SIGNATURES); #endif printf("Print information on a MAR file:\n"); printf(" mar [-H MARChannelID] [-V ProductVersion] [-C workingDir] " @@ -100,34 +108,39 @@ static int mar_test(const char *path) { int main(int argc, char **argv) { char *NSSConfigDir = NULL; const char *certNames[MAX_SIGNATURES]; char *MARChannelID = MAR_CHANNEL_ID; char *productVersion = MOZ_APP_VERSION; uint32_t i, k; int rv = -1; uint32_t certCount = 0; + uint32_t derCount = 0; int32_t sigIndex = -1; + char* DERFilePaths[MAX_SIGNATURES]; + #if defined(XP_WIN) && !defined(MAR_NSS) && !defined(NO_SIGN_VERIFY) HANDLE certFile; - /* We use DWORD here instead of uint64_t because it simplifies code with - the Win32 API ReadFile which takes a DWORD. DER files will not be too - large anyway. */ - DWORD fileSizes[MAX_SIGNATURES]; - DWORD read; uint8_t *certBuffers[MAX_SIGNATURES]; - char *DERFilePaths[MAX_SIGNATURES]; +#endif +#if !defined(NO_SIGN_VERIFY) && ((!defined(MAR_NSS) && defined(XP_WIN)) || \ + defined(XP_MACOSX)) + uint32_t fileSizes[MAX_SIGNATURES]; + uint32_t read; #endif memset(certNames, 0, sizeof(certNames)); #if defined(XP_WIN) && !defined(MAR_NSS) && !defined(NO_SIGN_VERIFY) + memset(certBuffers, 0, sizeof(certBuffers)); +#endif +#if !defined(NO_SIGN_VERIFY) && ((!defined(MAR_NSS) && defined(XP_WIN)) || \ + defined(XP_MACOSX)) memset(fileSizes, 0, sizeof(fileSizes)); - memset(certBuffers, 0, sizeof(certBuffers)); +#endif memset(DERFilePaths, 0, sizeof(DERFilePaths)); -#endif if (argc > 1 && 0 == strcmp(argv[1], "--version")) { print_version(); return 0; } if (argc < 3) { print_usage(); @@ -143,28 +156,29 @@ int main(int argc, char **argv) { argv[1][1] == 'I')) { break; /* -C workingdirectory */ } else if (argv[1][0] == '-' && argv[1][1] == 'C') { chdir(argv[2]); argv += 2; argc -= 2; } -#if defined(XP_WIN) && !defined(MAR_NSS) && !defined(NO_SIGN_VERIFY) +#if !defined(NO_SIGN_VERIFY) && ((!defined(MAR_NSS) && defined(XP_WIN)) || \ + defined(XP_MACOSX)) /* -D DERFilePath, also matches -D[index] DERFilePath We allow an index for verifying to be symmetric with the import and export command line arguments. */ else if (argv[1][0] == '-' && argv[1][1] == 'D' && - (argv[1][2] == (char)('0' + certCount) || argv[1][2] == '\0')) { - if (certCount >= MAX_SIGNATURES) { + (argv[1][2] == (char)('0' + derCount) || argv[1][2] == '\0')) { + if (derCount >= MAX_SIGNATURES) { print_usage(); return -1; } - DERFilePaths[certCount++] = argv[2]; + DERFilePaths[derCount++] = argv[2]; argv += 2; argc -= 2; } #endif /* -d NSSConfigdir */ else if (argv[1][0] == '-' && argv[1][1] == 'd') { NSSConfigDir = argv[2]; argv += 2; @@ -298,22 +312,22 @@ int main(int argc, char **argv) { print_usage(); return -1; } return import_signature(argv[2], sigIndex, argv[3], argv[4]); case 'v': #if defined(XP_WIN) && !defined(MAR_NSS) - if (certCount == 0) { + if (derCount == 0) { print_usage(); return -1; } - for (k = 0; k < certCount; ++k) { + for (k = 0; k < derCount; ++k) { /* If the mar program was built using CryptoAPI, then read in the buffer containing the cert from disk. */ certFile = CreateFileA(DERFilePaths[k], GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, @@ -330,47 +344,48 @@ int main(int argc, char **argv) { free(certBuffers[i]); } return -1; } CloseHandle(certFile); } rv = mar_verify_signatures(argv[2], certBuffers, fileSizes, - NULL, certCount); - for (k = 0; k < certCount; ++k) { + NULL, derCount); + for (k = 0; k < derCount; ++k) { free(certBuffers[k]); } if (rv) { /* Determine if the source MAR file has the new fields for signing */ int hasSignatureBlock; if (get_mar_file_info(argv[2], &hasSignatureBlock, NULL, NULL, NULL, NULL)) { fprintf(stderr, "ERROR: could not determine if MAR is old or new.\n"); } else if (!hasSignatureBlock) { fprintf(stderr, "ERROR: The MAR file is in the old format so has" " no signature to verify.\n"); } return -1; } return 0; + #else if (!NSSConfigDir || certCount == 0) { print_usage(); return -1; } if (NSSInitCryptoContext(NSSConfigDir)) { fprintf(stderr, "ERROR: Could not initialize crypto library.\n"); return -1; } - return mar_verify_signatures(argv[2], NULL, 0, - certNames, certCount); + return mar_verify_signatures(argv[2], (const uint8_t* const*)DERFilePaths, + 0, certNames, certCount); #endif /* defined(XP_WIN) && !defined(MAR_NSS) */ case 's': if (!NSSConfigDir || certCount == 0 || argc < 4) { print_usage(); return -1; } return mar_repackage_and_sign(NSSConfigDir, certNames, certCount,
new file mode 100644 --- /dev/null +++ b/modules/libmar/verify/MacVerifyCrypto.cpp @@ -0,0 +1,367 @@ +/* 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 <CoreFoundation/CoreFoundation.h> +#include <Security/Security.h> +#include <dlfcn.h> + +#include "cryptox.h" + +// We declare the necessary parts of the Security Transforms API here since +// we're building with the 10.6 SDK, which doesn't know about Security +// Transforms. +#ifdef __cplusplus +extern "C" { +#endif + const CFStringRef kSecTransformInputAttributeName = CFSTR("INPUT"); + typedef CFTypeRef SecTransformRef; + typedef struct OpaqueSecKeyRef* SecKeyRef; + + typedef SecTransformRef (*SecTransformCreateReadTransformWithReadStreamFunc) + (CFReadStreamRef inputStream); + SecTransformCreateReadTransformWithReadStreamFunc + SecTransformCreateReadTransformWithReadStreamPtr = NULL; + typedef CFTypeRef (*SecTransformExecuteFunc)(SecTransformRef transform, + CFErrorRef* error); + SecTransformExecuteFunc SecTransformExecutePtr = NULL; + typedef SecTransformRef (*SecVerifyTransformCreateFunc)(SecKeyRef key, + CFDataRef signature, + CFErrorRef* error); + SecVerifyTransformCreateFunc SecVerifyTransformCreatePtr = NULL; + typedef Boolean (*SecTransformSetAttributeFunc)(SecTransformRef transform, + CFStringRef key, + CFTypeRef value, + CFErrorRef* error); + SecTransformSetAttributeFunc SecTransformSetAttributePtr = NULL; + typedef SecCertificateRef (*SecCertificateCreateWithDataFunc) + (CFAllocatorRef allocator, + CFDataRef data); + SecCertificateCreateWithDataFunc SecCertificateCreateWithDataPtr = NULL; + typedef OSStatus (*SecCertificateCopyPublicKeyFunc) + (SecCertificateRef certificate, + SecKeyRef* key); + SecCertificateCopyPublicKeyFunc SecCertificateCopyPublicKeyPtr = NULL; +#ifdef __cplusplus +} +#endif + +#define MAC_OS_X_VERSION_10_7_HEX 0x00001070 + +static int sOnLionOrLater = -1; + +static bool OnLionOrLater() +{ + if (sOnLionOrLater < 0) { + SInt32 major = 0, minor = 0; + + CFURLRef url = + CFURLCreateWithString(kCFAllocatorDefault, + CFSTR("file:///System/Library/CoreServices/SystemVersion.plist"), + NULL); + CFReadStreamRef stream = + CFReadStreamCreateWithFile(kCFAllocatorDefault, url); + CFReadStreamOpen(stream); + CFDictionaryRef sysVersionPlist = (CFDictionaryRef) + CFPropertyListCreateWithStream(kCFAllocatorDefault, + stream, 0, kCFPropertyListImmutable, + NULL, NULL); + CFReadStreamClose(stream); + CFRelease(stream); + CFRelease(url); + + CFStringRef versionString = (CFStringRef) + CFDictionaryGetValue(sysVersionPlist, CFSTR("ProductVersion")); + CFArrayRef versions = + CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, + versionString, CFSTR(".")); + CFIndex count = CFArrayGetCount(versions); + if (count > 0) { + CFStringRef component = (CFStringRef) CFArrayGetValueAtIndex(versions, 0); + major = CFStringGetIntValue(component); + if (count > 1) { + component = (CFStringRef) CFArrayGetValueAtIndex(versions, 1); + minor = CFStringGetIntValue(component); + } + } + CFRelease(sysVersionPlist); + CFRelease(versions); + + if (major < 10) { + sOnLionOrLater = 0; + } else { + int version = 0x1000 + (minor << 4); + sOnLionOrLater = version >= MAC_OS_X_VERSION_10_7_HEX ? 1 : 0; + } + } + + return sOnLionOrLater > 0 ? true : false; +} + +CryptoX_Result +CryptoMac_InitCryptoProvider() +{ + if (!OnLionOrLater()) { + return CryptoX_Success; + } + + if (!SecTransformCreateReadTransformWithReadStreamPtr) { + SecTransformCreateReadTransformWithReadStreamPtr = + (SecTransformCreateReadTransformWithReadStreamFunc) + dlsym(RTLD_DEFAULT, "SecTransformCreateReadTransformWithReadStream"); + } + if (!SecTransformExecutePtr) { + SecTransformExecutePtr = (SecTransformExecuteFunc) + dlsym(RTLD_DEFAULT, "SecTransformExecute"); + } + if (!SecVerifyTransformCreatePtr) { + SecVerifyTransformCreatePtr = (SecVerifyTransformCreateFunc) + dlsym(RTLD_DEFAULT, "SecVerifyTransformCreate"); + } + if (!SecTransformSetAttributePtr) { + SecTransformSetAttributePtr = (SecTransformSetAttributeFunc) + dlsym(RTLD_DEFAULT, "SecTransformSetAttribute"); + } + if (!SecCertificateCreateWithDataPtr) { + SecCertificateCreateWithDataPtr = (SecCertificateCreateWithDataFunc) + dlsym(RTLD_DEFAULT, "SecCertificateCreateWithData"); + } + if (!SecCertificateCopyPublicKeyPtr) { + SecCertificateCopyPublicKeyPtr = (SecCertificateCopyPublicKeyFunc) + dlsym(RTLD_DEFAULT, "SecCertificateCopyPublicKey"); + } + if (!SecTransformCreateReadTransformWithReadStreamPtr || + !SecTransformExecutePtr || + !SecVerifyTransformCreatePtr || + !SecTransformSetAttributePtr || + !SecCertificateCreateWithDataPtr || + !SecCertificateCopyPublicKeyPtr) { + return CryptoX_Error; + } + return CryptoX_Success; +} + +CryptoX_Result +CryptoMac_VerifyBegin(CryptoX_SignatureHandle* aInputData, + CryptoX_PublicKey* aPublicKey) +{ + if (!OnLionOrLater()) { + return NSS_VerifyBegin((VFYContext**)aInputData, + (SECKEYPublicKey* const*)aPublicKey); + } + + (void)aPublicKey; + if (!aInputData) { + return CryptoX_Error; + } + + CryptoX_Result result = CryptoX_Error; + *aInputData = CFDataCreateMutable(kCFAllocatorDefault, 0); + if (*aInputData) { + result = CryptoX_Success; + } + + return result; +} + +CryptoX_Result +CryptoMac_VerifyUpdate(CryptoX_SignatureHandle* aInputData, void* aBuf, + unsigned int aLen) +{ + if (!OnLionOrLater()) { + return VFY_Update((VFYContext*)*aInputData, + (const unsigned char*)aBuf, aLen); + } + + if (aLen == 0) { + return CryptoX_Success; + } + if (!aInputData || !*aInputData) { + return CryptoX_Error; + } + + CryptoX_Result result = CryptoX_Error; + CFDataAppendBytes((CFMutableDataRef)*aInputData, (const UInt8 *) aBuf, aLen); + if (*aInputData) { + result = CryptoX_Success; + } + + return result; +} + +CryptoX_Result +CryptoMac_LoadPublicKey(const unsigned char* aCertData, + CryptoX_PublicKey* aPublicKey, + const char* aCertName, + CryptoX_Certificate* aCert) +{ + if (!aPublicKey || + (OnLionOrLater() && !aCertData) || + (!OnLionOrLater() && !aCertName)) { + return CryptoX_Error; + } + + if (!OnLionOrLater()) { + return NSS_LoadPublicKey(aCertName, + (SECKEYPublicKey**)aPublicKey, + (CERTCertificate**)aCert); + } + + CFURLRef url = + CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, + aCertData, + strlen((char*)aCertData), + false); + if (!url) { + return CryptoX_Error; + } + + CFReadStreamRef stream = CFReadStreamCreateWithFile(kCFAllocatorDefault, url); + if (!stream) { + CFRelease(url); + return CryptoX_Error; + } + + SecTransformRef readTransform = + SecTransformCreateReadTransformWithReadStreamPtr(stream); + if (!readTransform) { + CFRelease(url); + CFRelease(stream); + return CryptoX_Error; + } + + CFErrorRef error; + CFDataRef tempCertData = (CFDataRef)SecTransformExecutePtr(readTransform, + &error); + if (!tempCertData || error) { + CFRelease(url); + CFRelease(stream); + CFRelease(readTransform); + return CryptoX_Error; + } + + SecCertificateRef cert = SecCertificateCreateWithDataPtr(kCFAllocatorDefault, + tempCertData); + if (!cert) { + CFRelease(url); + CFRelease(stream); + CFRelease(readTransform); + CFRelease(tempCertData); + return CryptoX_Error; + } + + CryptoX_Result result = CryptoX_Error; + OSStatus status = SecCertificateCopyPublicKeyPtr(cert, + (SecKeyRef*)aPublicKey); + if (status == 0) { + result = CryptoX_Success; + } + + CFRelease(url); + CFRelease(stream); + CFRelease(readTransform); + CFRelease(tempCertData); + CFRelease(cert); + + return result; +} + +CryptoX_Result +CryptoMac_VerifySignature(CryptoX_SignatureHandle* aInputData, + CryptoX_PublicKey* aPublicKey, + const unsigned char* aSignature, + unsigned int aSignatureLen) +{ + if (!OnLionOrLater()) { + return NSS_VerifySignature((VFYContext* const*)aInputData, aSignature, + aSignatureLen); + } + + if (!aInputData || !*aInputData || !aPublicKey || !*aPublicKey || + !aSignature || aSignatureLen == 0) { + return CryptoX_Error; + } + + CFDataRef signatureData = CFDataCreate(kCFAllocatorDefault, + aSignature, aSignatureLen); + if (!signatureData) { + return CryptoX_Error; + } + + CFErrorRef error; + SecTransformRef verifier = + SecVerifyTransformCreatePtr((SecKeyRef)*aPublicKey, + signatureData, + &error); + if (!verifier || error) { + CFRelease(signatureData); + return CryptoX_Error; + } + + SecTransformSetAttributePtr(verifier, + kSecTransformInputAttributeName, + (CFDataRef)*aInputData, + &error); + if (error) { + CFRelease(signatureData); + CFRelease(verifier); + return CryptoX_Error; + } + + CryptoX_Result result = CryptoX_Error; + CFTypeRef rv = SecTransformExecutePtr(verifier, &error); + if (error) { + CFRelease(signatureData); + CFRelease(verifier); + return CryptoX_Error; + } + + if (CFGetTypeID(rv) == CFBooleanGetTypeID() && + CFBooleanGetValue((CFBooleanRef)rv) == true) { + result = CryptoX_Success; + } + + CFRelease(signatureData); + CFRelease(verifier); + + return result; +} + +void +CryptoMac_FreeSignatureHandle(CryptoX_SignatureHandle* aInputData) +{ + if (!OnLionOrLater()) { + return VFY_DestroyContext((VFYContext*)aInputData, PR_TRUE); + } + + if (!aInputData || !*aInputData) { + return; + } + CFRelease((CFMutableDataRef)*aInputData); +} + +void +CryptoMac_FreePublicKey(CryptoX_PublicKey* aPublicKey) +{ + if (!OnLionOrLater()) { + return SECKEY_DestroyPublicKey((SECKEYPublicKey*)*aPublicKey); + } + + if (!aPublicKey || !*aPublicKey) { + return; + } + CFRelease((SecKeyRef)*aPublicKey); +} + +void +CryptoMac_FreeCertificate(CryptoX_Certificate* aCertificate) +{ + if (!OnLionOrLater()) { + return CERT_DestroyCertificate((CERTCertificate*)*aCertificate); + } + + if (!aCertificate || !*aCertificate) { + return; + } + CFRelease((SecKeyRef)*aCertificate); +}
--- a/modules/libmar/verify/cryptox.h +++ b/modules/libmar/verify/cryptox.h @@ -12,27 +12,85 @@ #define CryptoX_Error (-1) #define CryptoX_Succeeded(X) ((X) == CryptoX_Success) #define CryptoX_Failed(X) ((X) != CryptoX_Success) #if defined(MAR_NSS) #include "nss_secutil.h" +#define CryptoX_InvalidHandleValue NULL +#define CryptoX_ProviderHandle void* + +#ifdef __cplusplus +extern "C" { +#endif CryptoX_Result NSS_LoadPublicKey(const char *certNickname, SECKEYPublicKey **publicKey, CERTCertificate **cert); CryptoX_Result NSS_VerifyBegin(VFYContext **ctx, SECKEYPublicKey * const *publicKey); CryptoX_Result NSS_VerifySignature(VFYContext * const *ctx , const unsigned char *signature, unsigned int signatureLen); +#ifdef __cplusplus +} // extern "C" +#endif -#define CryptoX_InvalidHandleValue NULL -#define CryptoX_ProviderHandle void* +#ifdef XP_MACOSX + +#define CryptoX_SignatureHandle void* +#define CryptoX_PublicKey void* +#define CryptoX_Certificate void* + +// Forward-declare Objective-C functions implemented in MacVerifyCrypto.mm. +#ifdef __cplusplus +extern "C" { +#endif +CryptoX_Result CryptoMac_InitCryptoProvider(); +CryptoX_Result CryptoMac_VerifyBegin(CryptoX_SignatureHandle* aInputData, + CryptoX_PublicKey* aPublicKey); +CryptoX_Result CryptoMac_VerifyUpdate(CryptoX_SignatureHandle* aInputData, + void* aBuf, unsigned int aLen); +CryptoX_Result CryptoMac_LoadPublicKey(const unsigned char* aCertData, + CryptoX_PublicKey* aPublicKey, + const char* aCertName, + CryptoX_Certificate* aCert); +CryptoX_Result CryptoMac_VerifySignature(CryptoX_SignatureHandle* aInputData, + CryptoX_PublicKey* aPublicKey, + const unsigned char* aSignature, + unsigned int aSignatureLen); +void CryptoMac_FreeSignatureHandle(CryptoX_SignatureHandle* aInputData); +void CryptoMac_FreePublicKey(CryptoX_PublicKey* aPublicKey); +void CryptoMac_FreeCertificate(CryptoX_Certificate* aCertificate); +#ifdef __cplusplus +} // extern "C" +#endif + +#define CryptoX_InitCryptoProvider(aCryptoHandle) \ + CryptoMac_InitCryptoProvider() +#define CryptoX_VerifyBegin(aCryptoHandle, aInputData, aPublicKey) \ + CryptoMac_VerifyBegin(aInputData, aPublicKey) +#define CryptoX_VerifyUpdate(aInputData, aBuf, aLen) \ + CryptoMac_VerifyUpdate(aInputData, aBuf, aLen) +#define CryptoX_LoadPublicKey(aCryptoHandle, aCertData, aDataSize, \ + aPublicKey, aCertName, aCert) \ + CryptoMac_LoadPublicKey(aCertData, aPublicKey, aCertName, aCert) +#define CryptoX_VerifySignature(aInputData, aPublicKey, aSignature, \ + aSignatureLen) \ + CryptoMac_VerifySignature(aInputData, aPublicKey, aSignature, aSignatureLen) +#define CryptoX_FreeSignatureHandle(aInputData) \ + CryptoMac_FreeSignatureHandle(aInputData) +#define CryptoX_FreePublicKey(aPublicKey) \ + CryptoMac_FreePublicKey(aPublicKey) +#define CryptoX_FreeCertificate(aCertificate) \ + CryptoMac_FreeCertificate(aCertificate) + +#else + #define CryptoX_SignatureHandle VFYContext * #define CryptoX_PublicKey SECKEYPublicKey * #define CryptoX_Certificate CERTCertificate * #define CryptoX_InitCryptoProvider(CryptoHandle) \ CryptoX_Success #define CryptoX_VerifyBegin(CryptoHandle, SignatureHandle, PublicKey) \ NSS_VerifyBegin(SignatureHandle, PublicKey) #define CryptoX_FreeSignatureHandle(SignatureHandle) \ @@ -44,16 +102,18 @@ CryptoX_Result NSS_VerifySignature(VFYCo NSS_LoadPublicKey(certName, publicKey, cert) #define CryptoX_VerifySignature(hash, publicKey, signedData, len) \ NSS_VerifySignature(hash, (const unsigned char *)(signedData), len) #define CryptoX_FreePublicKey(key) \ SECKEY_DestroyPublicKey(*key) #define CryptoX_FreeCertificate(cert) \ CERT_DestroyCertificate(*cert) +#endif + #elif defined(XP_WIN) #include <windows.h> #include <wincrypt.h> CryptoX_Result CryptoAPI_InitCryptoContext(HCRYPTPROV *provider); CryptoX_Result CryptoAPI_LoadPublicKey(HCRYPTPROV hProv, BYTE *certData,
--- a/modules/libmar/verify/moz.build +++ b/modules/libmar/verify/moz.build @@ -14,12 +14,17 @@ UNIFIED_SOURCES += [ FORCE_STATIC_LIB = True if CONFIG['OS_ARCH'] == 'WINNT': USE_STATIC_LIBS = True else: DEFINES['MAR_NSS'] = True LOCAL_INCLUDES += ['../sign'] +if CONFIG['OS_ARCH'] == 'Darwin': + UNIFIED_SOURCES += [ + 'MacVerifyCrypto.cpp', + ] + LOCAL_INCLUDES += [ '../src', ]
--- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -4021,16 +4021,21 @@ pref("layers.offmainthreadcomposition.fr pref("layers.async-video.enabled",false); #endif #ifdef MOZ_X11 // OMTC off by default on Linux, but if activated, use new textures and async-video. pref("layers.async-video.enabled", true); #endif +#ifdef MOZ_WIDGET_QT +pref("layers.offmainthreadcomposition.enabled", true); +pref("layers.async-video.enabled",true); +#endif + #ifdef XP_MACOSX pref("layers.offmainthreadcomposition.enabled", true); pref("layers.async-video.enabled",true); #endif // ANDROID covers android and b2g #ifdef ANDROID pref("layers.offmainthreadcomposition.enabled", true);
new file mode 100644 --- /dev/null +++ b/netwerk/base/public/NetStatistics.h @@ -0,0 +1,95 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim:set ts=4 sw=4 sts=4 et cin: */ +/* 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 NetStatistics_h__ +#define NetStatistics_h__ + +#include "mozilla/Assertions.h" + +#include "nsCOMPtr.h" +#include "nsError.h" +#include "nsINetworkManager.h" +#include "nsINetworkStatsServiceProxy.h" +#include "nsThreadUtils.h" +#include "nsProxyRelease.h" + +namespace mozilla { +namespace net { + +// The following members are used for network per-app metering. +const static uint64_t NETWORK_STATS_THRESHOLD = 65536; +const static char NETWORK_STATS_NO_SERVICE_TYPE[] = ""; + +inline nsresult +GetActiveNetworkInterface(nsCOMPtr<nsINetworkInterface> &aNetworkInterface) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsresult rv; + nsCOMPtr<nsINetworkManager> networkManager = + do_GetService("@mozilla.org/network/manager;1", &rv); + + if (NS_FAILED(rv) || !networkManager) { + aNetworkInterface = nullptr; + return rv; + } + + networkManager->GetActive(getter_AddRefs(aNetworkInterface)); + + return NS_OK; +} + +class SaveNetworkStatsEvent : public nsRunnable { +public: + SaveNetworkStatsEvent(uint32_t aAppId, + nsMainThreadPtrHandle<nsINetworkInterface> &aActiveNetwork, + uint64_t aCountRecv, + uint64_t aCountSent, + bool aIsAccumulative) + : mAppId(aAppId), + mActiveNetwork(aActiveNetwork), + mCountRecv(aCountRecv), + mCountSent(aCountSent), + mIsAccumulative(aIsAccumulative) + { + MOZ_ASSERT(mAppId != NECKO_NO_APP_ID); + MOZ_ASSERT(mActiveNetwork); + } + + NS_IMETHOD Run() + { + MOZ_ASSERT(NS_IsMainThread()); + + nsresult rv; + nsCOMPtr<nsINetworkStatsServiceProxy> mNetworkStatsServiceProxy = + do_GetService("@mozilla.org/networkstatsServiceProxy;1", &rv); + if (NS_FAILED(rv)) { + return rv; + } + + // save the network stats through NetworkStatsServiceProxy + mNetworkStatsServiceProxy->SaveAppStats(mAppId, + mActiveNetwork, + PR_Now() / 1000, + mCountRecv, + mCountSent, + mIsAccumulative, + nullptr); + + return NS_OK; + } +private: + uint32_t mAppId; + nsMainThreadPtrHandle<nsINetworkInterface> mActiveNetwork; + uint64_t mCountRecv; + uint64_t mCountSent; + bool mIsAccumulative; +}; + +} // namespace mozilla:net +} // namespace mozilla + +#endif // !NetStatistics_h__
--- a/netwerk/base/public/moz.build +++ b/netwerk/base/public/moz.build @@ -136,10 +136,15 @@ EXPORTS += [ 'nsChannelProperties.h', 'nsNetStrings.h', 'nsNetUtil.h', 'nsReadLine.h', 'nsStreamListenerWrapper.h', 'nsURIHashKey.h', ] +if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk': + EXPORTS += [ + 'NetStatistics.h', + ] + FAIL_ON_WARNINGS = True
--- a/netwerk/base/public/nsNetUtil.h +++ b/netwerk/base/public/nsNetUtil.h @@ -81,21 +81,16 @@ #include "nsIOfflineCacheUpdate.h" #include "nsIContentSniffer.h" #include "nsCategoryCache.h" #include "nsStringStream.h" #include "nsIViewSourceChannel.h" #include <limits> -#ifdef MOZ_WIDGET_GONK -#include "nsINetworkManager.h" -#include "nsThreadUtils.h" // for NS_IsMainThread -#endif - #ifdef MOZILLA_INTERNAL_API #include "nsReadableUtils.h" inline already_AddRefed<nsIIOService> do_GetIOService(nsresult* error = 0) { nsCOMPtr<nsIIOService> io = mozilla::services::GetIOService(); @@ -2399,34 +2394,9 @@ NS_IsSrcdocChannel(nsIChannel *aChannel) nsCOMPtr<nsIViewSourceChannel> vsc = do_QueryInterface(aChannel); if (vsc) { vsc->GetIsSrcdocChannel(&isSrcdoc); return isSrcdoc; } return false; } -// The following members are used for network per-app metering. -const static uint64_t NETWORK_STATS_THRESHOLD = 65536; -const static char NETWORK_STATS_NO_SERVICE_TYPE[] = ""; - -#ifdef MOZ_WIDGET_GONK -inline nsresult -NS_GetActiveNetworkInterface(nsCOMPtr<nsINetworkInterface> &aNetworkInterface) -{ - MOZ_ASSERT(NS_IsMainThread()); - - nsresult rv; - nsCOMPtr<nsINetworkManager> networkManager = - do_GetService("@mozilla.org/network/manager;1", &rv); - - if (NS_FAILED(rv) || !networkManager) { - aNetworkInterface = nullptr; - return rv; - } - - networkManager->GetActive(getter_AddRefs(aNetworkInterface)); - - return NS_OK; -} -#endif - #endif // !nsNetUtil_h__
--- a/netwerk/protocol/ftp/nsFtpConnectionThread.cpp +++ b/netwerk/protocol/ftp/nsFtpConnectionThread.cpp @@ -38,17 +38,17 @@ #include "nsIProtocolHandler.h" #include "nsIProxyInfo.h" #include "nsIRunnable.h" #include "nsISocketTransportService.h" #include "nsIURI.h" #include "nsICacheSession.h" #ifdef MOZ_WIDGET_GONK -#include "nsINetworkStatsServiceProxy.h" +#include "NetStatistics.h" #endif #if defined(PR_LOGGING) extern PRLogModuleInfo* gFTPLog; #endif #define LOG(args) PR_LOG(gFTPLog, PR_LOG_DEBUG, args) #define LOG_ALWAYS(args) PR_LOG(gFTPLog, PR_LOG_ALWAYS, args) @@ -1686,17 +1686,20 @@ nsFtpState::Init(nsFtpChannel *channel) NS_ASSERTION(channel, "FTP: needs a channel"); mChannel = channel; // a straight ref ptr to the channel // initialize counter for network metering mCountRecv = 0; #ifdef MOZ_WIDGET_GONK - NS_GetActiveNetworkInterface(mActiveNetwork); + nsCOMPtr<nsINetworkInterface> activeNetwork; + GetActiveNetworkInterface(activeNetwork); + mActiveNetwork = + new nsMainThreadPtrHolder<nsINetworkInterface>(activeNetwork); #endif mKeepRunning = true; mSuppliedEntityID = channel->EntityID(); if (channel->UploadStream()) mAction = PUT; @@ -2213,30 +2216,21 @@ nsFtpState::SaveNetworkStats(bool enforc // If |enforce| is false, the traffic amount is saved // only when the total amount exceeds the predefined // threshold. if (!enforce && mCountRecv < NETWORK_STATS_THRESHOLD) { return NS_OK; } - nsresult rv; - nsCOMPtr<nsINetworkStatsServiceProxy> networkStatsServiceProxy = - do_GetService("@mozilla.org/networkstatsServiceProxy;1", &rv); - if (NS_FAILED(rv)) { - return rv; - } - - networkStatsServiceProxy->SaveAppStats(appId, - mActiveNetwork, - PR_Now() / 1000, - mCountRecv, - 0, - false, - nullptr); + // Create the event to save the network statistics. + // the event is then dispathed to the main thread. + nsRefPtr<nsR