From bc2f5ddc94f322f2aa0b16955846b1461427fbdb Mon Sep 17 00:00:00 2001 From: Gavin Sharp Date: Tue, 20 Aug 2013 14:16:25 -0700 Subject: [PATCH 01/54] Bug 907404: disable the in-product whatsnew page, so that manual upgrades are consistent with updater upgrades wrt showing the whatsnew page (updater upgrades disable whatsnew via snippets), r=dolske, a=asa --HG-- extra : transplant_source : %0D6A%BEP%99%09%CEVA%00l%A7%27%C3%08%EF%03%03%82 --- browser/branding/official/pref/firefox-branding.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/branding/official/pref/firefox-branding.js b/browser/branding/official/pref/firefox-branding.js index b8856dbf0fb9..abc07038c0bc 100644 --- a/browser/branding/official/pref/firefox-branding.js +++ b/browser/branding/official/pref/firefox-branding.js @@ -2,7 +2,7 @@ * 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/. */ -pref("startup.homepage_override_url","https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/whatsnew/?oldversion=%OLD_VERSION%"); +pref("startup.homepage_override_url",""); pref("startup.homepage_welcome_url","https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/firstrun/"); // Interval: Time between checks for a new version (in seconds) pref("app.update.interval", 43200); // 12 hours From 2d50bc9ecdcd73466cacd466f7bc0e16631f9f76 Mon Sep 17 00:00:00 2001 From: Justin Dolske Date: Wed, 21 Aug 2013 17:25:03 -0700 Subject: [PATCH 02/54] Bug 355063 - Password manager does not work on script-generated forms, should use new DOMFormHasPassword event instead. r=mattn --- browser/base/content/content.js | 1 + modules/libpref/src/init/all.js | 1 + .../passwordmgr/LoginManagerContent.jsm | 70 ++++++++++++++++++- .../components/passwordmgr/test/Makefile.in | 1 + ...est_basic_form_observer_autofillForms.html | 59 ++++++++-------- .../test/test_basic_form_pwevent.html | 66 +++++++++++++++++ .../passwordmgr/test/test_maxforms_1.html | 7 +- .../passwordmgr/test/test_maxforms_2.html | 7 +- .../passwordmgr/test/test_maxforms_3.html | 7 +- 9 files changed, 184 insertions(+), 35 deletions(-) create mode 100644 toolkit/components/passwordmgr/test/test_basic_form_pwevent.html diff --git a/browser/base/content/content.js b/browser/base/content/content.js index c75a8a40e353..824b29a2ac4d 100644 --- a/browser/base/content/content.js +++ b/browser/base/content/content.js @@ -48,6 +48,7 @@ if (Services.prefs.getBoolPref("browser.tabs.remote")) { }); addEventListener("DOMFormHasPassword", function(event) { InsecurePasswordUtils.checkForInsecurePasswords(event.target); + LoginManagerContent.onFormPassword(event); }); addEventListener("DOMAutoComplete", function(event) { LoginManagerContent.onUsernameInput(event); diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index 98ed61d1ee8f..ea9f78fb52f1 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -3909,6 +3909,7 @@ pref("signon.SignonFileName3", "signons3.txt"); // obsolete pref("signon.autofillForms", true); pref("signon.autologin.proxy", false); pref("signon.debug", false); +pref("signon.useDOMFormHasPassword", true); // Satchel (Form Manager) prefs pref("browser.formfill.debug", false); diff --git a/toolkit/components/passwordmgr/LoginManagerContent.jsm b/toolkit/components/passwordmgr/LoginManagerContent.jsm index 75f24cee19e2..f22bacf9bee8 100644 --- a/toolkit/components/passwordmgr/LoginManagerContent.jsm +++ b/toolkit/components/passwordmgr/LoginManagerContent.jsm @@ -13,7 +13,8 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); -var gEnabled = false, gDebug = false; // these mirror signon.* prefs +var gEnabled = false, gDebug = false, gAutofillForms = true; // these mirror signon.* prefs +var gUseDOMFormHasPassword = false; // use DOMFormHasPassword event for autofill function log(...pieces) { function generateLogMessage(args) { @@ -70,6 +71,8 @@ var observer = { onPrefChange : function() { gDebug = Services.prefs.getBoolPref("signon.debug"); gEnabled = Services.prefs.getBoolPref("signon.rememberSignons"); + gAutofillForms = Services.prefs.getBoolPref("signon.autofillForms"); + gUseDOMFormHasPassword = Services.prefs.getBoolPref("signon.useDOMFormHasPassword"); }, }; @@ -92,6 +95,10 @@ var LoginManagerContent = { }, onContentLoaded : function (event) { + // If we're using the new DOMFormHasPassword event, don't fill at pageload. + if (gUseDOMFormHasPassword) + return; + if (!event.isTrusted) return; @@ -108,6 +115,64 @@ var LoginManagerContent = { }, + onFormPassword: function (event) { + // If we're not using the new DOMFormHasPassword event, only fill at pageload. + if (!gUseDOMFormHasPassword) + return; + + if (!event.isTrusted) + return; + + if (!gEnabled) + return; + + let form = event.target; + let doc = form.ownerDocument; + + log("onFormPassword for", doc.documentURI); + + // If there are no logins for this site, bail out now. + let formOrigin = LoginUtils._getPasswordOrigin(doc.documentURI); + if (!Services.logins.countLogins(formOrigin, "", null)) + return; + + // If we're currently displaying a master password prompt, defer + // processing this form until the user handles the prompt. + if (Services.logins.uiBusy) { + log("deferring onFormPassword for", doc.documentURI); + let self = this; + let observer = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]), + + observe: function (subject, topic, data) { + log("Got deferred onFormPassword notification:", topic); + // Only run observer once. + Services.obs.removeObserver(this, "passwordmgr-crypto-login"); + Services.obs.removeObserver(this, "passwordmgr-crypto-loginCanceled"); + if (topic == "passwordmgr-crypto-loginCanceled") + return; + self.onFormPassword(event); + }, + handleEvent : function (event) { + // Not expected to be called + } + }; + // Trickyness follows: We want an observer, but don't want it to + // cause leaks. So add the observer with a weak reference, and use + // a dummy event listener (a strong reference) to keep it alive + // until the form is destroyed. + Services.obs.addObserver(observer, "passwordmgr-crypto-login", true); + Services.obs.addObserver(observer, "passwordmgr-crypto-loginCanceled", true); + form.addEventListener("mozCleverClosureHack", observer); + return; + } + + let autofillForm = gAutofillForms && !PrivateBrowsingUtils.isWindowPrivate(doc.defaultView); + + this._fillForm(form, autofillForm, false, false, null); + }, + + /* * onUsernameInput * @@ -537,8 +602,7 @@ var LoginManagerContent = { log("fillDocument processing", forms.length, "forms on", doc.documentURI); - var autofillForm = !PrivateBrowsingUtils.isWindowPrivate(doc.defaultView) && - Services.prefs.getBoolPref("signon.autofillForms"); + var autofillForm = gAutofillForms && !PrivateBrowsingUtils.isWindowPrivate(doc.defaultView); var previousActionOrigin = null; var foundLogins = null; diff --git a/toolkit/components/passwordmgr/test/Makefile.in b/toolkit/components/passwordmgr/test/Makefile.in index 1eced10a9d61..0852f8bf88ad 100644 --- a/toolkit/components/passwordmgr/test/Makefile.in +++ b/toolkit/components/passwordmgr/test/Makefile.in @@ -27,6 +27,7 @@ MOCHITEST_FILES = \ test_basic_form_observer_autocomplete.html \ test_basic_form_observer_foundLogins.html \ test_basic_form_pwonly.html \ + test_basic_form_pwevent.html \ test_bug_227640.html \ test_bug_242956.html \ test_bug_360493_1.html \ diff --git a/toolkit/components/passwordmgr/test/test_basic_form_observer_autofillForms.html b/toolkit/components/passwordmgr/test/test_basic_form_observer_autofillForms.html index 199d810671f8..552b7930eccb 100644 --- a/toolkit/components/passwordmgr/test/test_basic_form_observer_autofillForms.html +++ b/toolkit/components/passwordmgr/test/test_basic_form_observer_autofillForms.html @@ -21,6 +21,36 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm"); // Assume that the pref starts out true, so set to false SpecialPowers.setBoolPref("signon.autofillForms", false); + +var TestObserver = { + receivedNotificationFoundForm : false, + receivedNotificationFoundLogins : false, + dataFoundForm : "", + dataFoundLogins : null, + QueryInterface : XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]), + observe : function (subject, topic, data) { + netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); + var pwmgr = Cc["@mozilla.org/login-manager;1"]. + getService(Ci.nsILoginManager); + if (topic == "passwordmgr-found-form") { + info("got passwordmgr-found-form"); + this.receivedNotificationFoundForm = true; + this.dataFoundForm = data; + // Now fill the form + pwmgr.fillForm(subject); + } else if (topic == "passwordmgr-found-logins") { + info("got passwordmgr-found-logins"); + this.receivedNotificationFoundLogins = true; + this.dataFoundLogins = subject.QueryInterface(Ci.nsIPropertyBag2); + } + } +}; + +// Add the observer +var os = Cc["@mozilla.org/observer-service;1"]. + getService(Ci.nsIObserverService); +os.addObserver(TestObserver, "passwordmgr-found-form", false); +os.addObserver(TestObserver, "passwordmgr-found-logins", false);

@@ -42,35 +72,6 @@ SpecialPowers.setBoolPref("signon.autofillForms", false); + + + + + +Mozilla Bug 355063 +

+
+forms go here! +
+
+
+ + diff --git a/toolkit/components/passwordmgr/test/test_maxforms_1.html b/toolkit/components/passwordmgr/test/test_maxforms_1.html index d7fa80c52f0a..bcc2e29660cf 100644 --- a/toolkit/components/passwordmgr/test/test_maxforms_1.html +++ b/toolkit/components/passwordmgr/test/test_maxforms_1.html @@ -52,7 +52,12 @@ function startTest() { SimpleTest.finish(); } -window.onload = startTest; +if (SpecialPowers.getBoolPref("signon.useDOMFormHasPassword")) { + info("skipping test when signon.useDOMFormHasPassword is enabled"); + SimpleTest.finish(); +} else { + window.onload = startTest; +} diff --git a/toolkit/components/passwordmgr/test/test_maxforms_2.html b/toolkit/components/passwordmgr/test/test_maxforms_2.html index 281f12d17a1d..da315c420294 100644 --- a/toolkit/components/passwordmgr/test/test_maxforms_2.html +++ b/toolkit/components/passwordmgr/test/test_maxforms_2.html @@ -52,7 +52,12 @@ function startTest() { SimpleTest.finish(); } -window.onload = startTest; +if (SpecialPowers.getBoolPref("signon.useDOMFormHasPassword")) { + info("skipping test when signon.useDOMFormHasPassword is enabled"); + SimpleTest.finish(); +} else { + window.onload = startTest; +} diff --git a/toolkit/components/passwordmgr/test/test_maxforms_3.html b/toolkit/components/passwordmgr/test/test_maxforms_3.html index 84fa863f42f3..63d2bfedd497 100644 --- a/toolkit/components/passwordmgr/test/test_maxforms_3.html +++ b/toolkit/components/passwordmgr/test/test_maxforms_3.html @@ -52,7 +52,12 @@ function startTest() { SimpleTest.finish(); } -window.onload = startTest; +if (SpecialPowers.getBoolPref("signon.useDOMFormHasPassword")) { + info("skipping test when signon.useDOMFormHasPassword is enabled"); + SimpleTest.finish(); +} else { + window.onload = startTest; +} From 41fb494804ff26591456810e9bb9eaf815f37b6f Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Thu, 22 Aug 2013 09:45:14 +1000 Subject: [PATCH 03/54] Bug 907821 - Fix intermittent orange in browser_chat_tearoff.js. r=mixedpuppy --HG-- extra : rebase_source : 93f508349faea2e1d22ab91060f7a8d97a58b301 --- .../test/social/browser_chat_tearoff.js | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/browser/base/content/test/social/browser_chat_tearoff.js b/browser/base/content/test/social/browser_chat_tearoff.js index 87fd31e8f50e..5906f73d8c35 100644 --- a/browser/base/content/test/social/browser_chat_tearoff.js +++ b/browser/base/content/test/social/browser_chat_tearoff.js @@ -56,6 +56,7 @@ var tests = { doc.body.appendChild(div); let swap = document.getAnonymousElementByAttribute(chats.selectedChat, "anonid", "swap"); swap.click(); + port.close(); break; case "got-chatbox-message": ok(true, "got chatbox message"); @@ -73,13 +74,16 @@ var tests = { .getInterface(Components.interfaces.nsIDOMWindow); Services.wm.removeListener(this); // wait for load to ensure the window is ready for us to test - domwindow.addEventListener("load", function _load() { + domwindow.addEventListener("load", function _load(event) { let doc = domwindow.document; - if (doc.location.href != "chrome://browser/content/chatWindow.xul") - return; + if (event.target != doc) + return; + domwindow.removeEventListener("load", _load, false); - domwindow.addEventListener("unload", function _close() { + domwindow.addEventListener("unload", function _close(event) { + if (event.target != doc) + return; domwindow.removeEventListener("unload", _close, false); info("window has been closed"); waitForCondition(function() { @@ -160,13 +164,15 @@ var tests = { Services.wm.removeListener(this); // wait for load to ensure the window is ready for us to test, make sure // we're not getting called for about:blank - domwindow.addEventListener("load", function _load() { + domwindow.addEventListener("load", function _load(event) { let doc = domwindow.document; - if (doc.location.href != "chrome://browser/content/chatWindow.xul") - return; + if (event.target != doc) + return; domwindow.removeEventListener("load", _load, false); - domwindow.addEventListener("unload", function _close() { + domwindow.addEventListener("unload", function _close(event) { + if (event.target != doc) + return; domwindow.removeEventListener("unload", _close, false); ok(true, "window has been closed"); next(); @@ -184,6 +190,7 @@ var tests = { },function() { // logout, we should get unload next port.postMessage({topic: "test-logout"}); + port.close(); }, domwindow); }, false); From 531820f87c847f74d62f53fbdf446ffcc41d9830 Mon Sep 17 00:00:00 2001 From: Rodrigo Silveira Date: Mon, 29 Jul 2013 11:01:36 -0700 Subject: [PATCH 04/54] Bug 903178 - Start screen should scroll vertically when in portrait mode r=sfoster --- browser/metro/base/content/bindings/grid.xml | 31 +++++++++++-------- .../base/content/startui/BookmarksView.js | 4 --- .../metro/base/content/startui/HistoryView.js | 4 --- .../base/content/startui/RemoteTabsView.js | 5 +-- browser/metro/base/content/startui/StartUI.js | 23 ++------------ .../base/content/startui/TopSitesView.js | 4 --- browser/metro/modules/View.jsm | 13 ++++++-- browser/metro/theme/browser.css | 7 ++++- browser/metro/theme/defines.inc | 1 + browser/metro/theme/platform.css | 1 + browser/metro/theme/tiles.css | 2 +- 11 files changed, 41 insertions(+), 54 deletions(-) diff --git a/browser/metro/base/content/bindings/grid.xml b/browser/metro/base/content/bindings/grid.xml index 744b10765882..6042b9eeaa1c 100644 --- a/browser/metro/base/content/bindings/grid.xml +++ b/browser/metro/base/content/bindings/grid.xml @@ -463,21 +463,26 @@ // clear explicit width and columns before calculating from avail. height again let gridStyle = this._grid.style; - gridStyle.removeProperty('min-width'); - gridStyle.removeProperty('-moz-column-count'); + gridStyle.removeProperty("min-width"); + gridStyle.removeProperty("-moz-column-count"); - // We favor overflowing horizontally, not vertically (rows then colums) - // rows attribute = max rows - let maxRowCount = Math.min(this.getAttribute("rows") || Infinity, Math.floor(containerDims.height / itemDims.height)); - this._rowCount = Math.min(this.itemCount, maxRowCount); + if (this.hasAttribute("vertical")) { + this._columnCount = Math.floor(containerDims.width / itemDims.width) || 1; + this._rowCount = Math.floor(this.itemCount / this._columnCount); + } else { + // We favor overflowing horizontally, not vertically (rows then colums) + // rows attribute = max rows + let maxRowCount = Math.min(this.getAttribute("rows") || Infinity, Math.floor(containerDims.height / itemDims.height)); + this._rowCount = Math.min(this.itemCount, maxRowCount); - // columns attribute = min cols - this._columnCount = this.itemCount ? - Math.max( - // at least 1 column when there are items - this.getAttribute("columns") || 1, - Math.ceil(this.itemCount / this._rowCount) - ) : this.getAttribute("columns") || 0; + // columns attribute = min cols + this._columnCount = this.itemCount ? + Math.max( + // at least 1 column when there are items + this.getAttribute("columns") || 1, + Math.ceil(this.itemCount / this._rowCount) + ) : this.getAttribute("columns") || 0; + } // width is typically auto, cap max columns by truncating items collection // or, setting max-width style property with overflow hidden diff --git a/browser/metro/base/content/startui/BookmarksView.js b/browser/metro/base/content/startui/BookmarksView.js index a3cf34279e7c..83eac5b6e1bb 100644 --- a/browser/metro/base/content/startui/BookmarksView.js +++ b/browser/metro/base/content/startui/BookmarksView.js @@ -323,10 +323,6 @@ let BookmarksStartView = { this._view.destruct(); } }, - - show: function show() { - this._grid.arrangeItems(); - } }; /** diff --git a/browser/metro/base/content/startui/HistoryView.js b/browser/metro/base/content/startui/HistoryView.js index 42b100541ce1..efd46487a24e 100644 --- a/browser/metro/base/content/startui/HistoryView.js +++ b/browser/metro/base/content/startui/HistoryView.js @@ -302,10 +302,6 @@ let HistoryStartView = { _view: null, get _grid() { return document.getElementById("start-history-grid"); }, - show: function show() { - this._grid.arrangeItems(); - }, - init: function init() { this._view = new HistoryView(this._grid, StartUI.maxResultsPerSection, true); this._view.populateGrid(); diff --git a/browser/metro/base/content/startui/RemoteTabsView.js b/browser/metro/base/content/startui/RemoteTabsView.js index 6cd321b0f4fc..d9715d7dda55 100644 --- a/browser/metro/base/content/startui/RemoteTabsView.js +++ b/browser/metro/base/content/startui/RemoteTabsView.js @@ -98,6 +98,7 @@ RemoteTabsView.prototype = Util.extend(Object.create(View.prototype), { }, this); } this.setUIAccessVisible(show); + this._set.arrangeItems(); }, destruct: function destruct() { @@ -127,8 +128,4 @@ let RemoteTabsStartView = { this._view.destruct(); } }, - - show: function show() { - this._grid.arrangeItems(); - } }; diff --git a/browser/metro/base/content/startui/StartUI.js b/browser/metro/base/content/startui/StartUI.js index 0647dbec4d29..7cbc43d7dfb0 100644 --- a/browser/metro/base/content/startui/StartUI.js +++ b/browser/metro/base/content/startui/StartUI.js @@ -6,10 +6,6 @@ Cu.import("resource://gre/modules/Services.jsm"); -// When setting the max-height of the start tab contents, this is the buffer we subtract -// for the nav bar plus white space above it. -const kBottomContentMargin = 50; - var StartUI = { get startUI() { return document.getElementById("start-container"); }, @@ -30,7 +26,6 @@ var StartUI = { document.getElementById("bcast_preciseInput").setAttribute("input", this.chromeWin.InputSourceHelper.isPrecise ? "precise" : "imprecise"); - this._updateStartHeight(); this._adjustDOMforViewState(); TopSitesStartView.init(); @@ -38,12 +33,6 @@ var StartUI = { HistoryStartView.init(); RemoteTabsStartView.init(); - TopSitesStartView.show(); - BookmarksStartView.show(); - HistoryStartView.show(); - RemoteTabsStartView.show(); - - this.chromeWin.document.getElementById("browsers").addEventListener("SizeChanged", this, true); this.chromeWin.addEventListener("MozPrecisePointer", this, true); this.chromeWin.addEventListener("MozImprecisePointer", this, true); Services.obs.addObserver(this, "metro_viewstate_changed", false); @@ -60,7 +49,6 @@ var StartUI = { RemoteTabsStartView.uninit(); if (this.chromeWin) { - this.chromeWin.document.getElementById("browsers").removeEventListener("SizeChanged", this, true); this.chromeWin.removeEventListener("MozPrecisePointer", this, true); this.chromeWin.removeEventListener("MozImprecisePointer", this, true); } @@ -108,7 +96,8 @@ var StartUI = { this.onClick(aEvent); break; case "MozMousePixelScroll": - if (this.startUI.getAttribute("viewstate") == "snapped") { + let viewstate = this.startUI.getAttribute("viewstate"); + if (viewstate === "snapped" || viewstate === "portrait") { window.scrollBy(0, aEvent.detail); } else { window.scrollBy(aEvent.detail, 0); @@ -117,17 +106,9 @@ var StartUI = { aEvent.preventDefault(); aEvent.stopPropagation(); break; - case "SizeChanged": - this._updateStartHeight(); - break; } }, - _updateStartHeight: function () { - document.getElementById("start-container").style.maxHeight = - (this.chromeWin.ContentAreaObserver.contentHeight - kBottomContentMargin) + "px"; - }, - _adjustDOMforViewState: function() { if (this.chromeWin.MetroUtils.immersive) { let currViewState = ""; diff --git a/browser/metro/base/content/startui/TopSitesView.js b/browser/metro/base/content/startui/TopSitesView.js index eebf9b9b319a..19458115e5bc 100644 --- a/browser/metro/base/content/startui/TopSitesView.js +++ b/browser/metro/base/content/startui/TopSitesView.js @@ -311,8 +311,4 @@ let TopSitesStartView = { this._view.destruct(); } }, - - show: function show() { - this._grid.arrangeItems(); - } }; diff --git a/browser/metro/modules/View.jsm b/browser/metro/modules/View.jsm index d9968bb25c4c..5c9e6ec09833 100644 --- a/browser/metro/modules/View.jsm +++ b/browser/metro/modules/View.jsm @@ -29,9 +29,18 @@ function View() { View.prototype = { _adjustDOMforViewState: function _adjustDOMforViewState(aState) { if (this._set) { - if (undefined == aState) - aState = this._set.getAttribute("viewstate"); + if (undefined == aState) + aState = this._set.getAttribute("viewstate"); + this._set.setAttribute("suppressonselect", (aState == "snapped")); + + if (aState == "portrait") { + this._set.setAttribute("vertical", true); + } else { + this._set.removeAttribute("vertical"); + } + + this._set.arrangeItems(); } }, diff --git a/browser/metro/theme/browser.css b/browser/metro/theme/browser.css index 72617e3b4d61..eb8e69363523 100644 --- a/browser/metro/theme/browser.css +++ b/browser/metro/theme/browser.css @@ -190,15 +190,19 @@ documenttab[selected] .documenttab-selection { #startui-page { overflow-x: scroll; overflow-y: hidden; + height: 100%; } #startui-body { height: 100%; + margin: 0; } #start-container { display: -moz-box; min-width: @grid_double_column_width@; + height: 100%; + width: 100%; } #start-topsites { @@ -210,7 +214,8 @@ documenttab[selected] .documenttab-selection { padding-bottom: @toolbar_height@; } -#start-container[viewstate="snapped"] { +#start-container[viewstate="snapped"], +#start-container[viewstate="portrait"] { -moz-box-orient: vertical; } diff --git a/browser/metro/theme/defines.inc b/browser/metro/theme/defines.inc index 035b6fff42ae..fa11961709c2 100644 --- a/browser/metro/theme/defines.inc +++ b/browser/metro/theme/defines.inc @@ -44,6 +44,7 @@ %define tile_border_color #dbdcde %define tile_spacing 12px +%define tile_side_margin 6px %define scroller_thickness 4px %define scroller_minimum 8px diff --git a/browser/metro/theme/platform.css b/browser/metro/theme/platform.css index 3819fb428df7..6445542e95c3 100644 --- a/browser/metro/theme/platform.css +++ b/browser/metro/theme/platform.css @@ -624,6 +624,7 @@ arrowbox { } .meta-section-title { + margin: @metro_spacing_normal@ @tile_side_margin@; font-size: @metro_font_large@; font-weight: 100; cursor: default; diff --git a/browser/metro/theme/tiles.css b/browser/metro/theme/tiles.css index 58db1bc31d18..f93d6a56a883 100644 --- a/browser/metro/theme/tiles.css +++ b/browser/metro/theme/tiles.css @@ -68,7 +68,7 @@ richgriditem { background-origin: padding-box; /* content positioning within the grid "cell" gives us the gutters/spacing between tiles */ - top: 2px; right: 6px; bottom: 10px; left: 6px; + top: 2px; right: @tile_side_margin@; bottom: 10px; left: @tile_side_margin@; border: @metro_border_thin@ solid @tile_border_color@; box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.1); transition: 150ms transform ease-out; From 2585de9a07cff5734b765a35ed14c5c8932a8d6b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sat, 17 Aug 2013 15:50:18 -0700 Subject: [PATCH 05/54] Bug 905017 (part 1) - Minimize inclusions of JS engine headers in .h and .idl files. r=billm. --HG-- extra : rebase_source : 984c61ab12f46be0509b1ce0d458d9a6e5841c64 --- caps/include/nsScriptSecurityManager.h | 6 +++-- content/base/public/nsIDOMFile.idl | 2 -- content/base/public/nsIXMLHttpRequest.idl | 5 ----- content/base/src/WebSocket.h | 1 - content/base/src/nsFrameMessageManager.h | 3 ++- content/base/src/nsInProcessTabChildGlobal.h | 1 - content/base/src/nsXMLHttpRequest.h | 1 - content/canvas/src/ImageData.h | 2 -- content/canvas/src/WebGLActiveInfo.h | 7 +++++- content/events/src/nsDOMMessageEvent.h | 1 - content/events/src/nsEventListenerManager.h | 1 - .../content/src/HTMLPropertiesCollection.h | 1 - content/html/content/src/nsDOMStringMap.h | 1 - content/html/document/src/nsHTMLDocument.h | 1 - content/xbl/src/nsXBLBinding.h | 1 - content/xbl/src/nsXBLMaybeCompiled.h | 2 -- content/xbl/src/nsXBLProtoImplMember.h | 1 - content/xbl/src/nsXBLProtoImplMethod.h | 1 - content/xbl/src/nsXBLProtoImplProperty.h | 1 - content/xbl/src/nsXBLPrototypeHandler.h | 6 +++-- content/xbl/src/nsXBLSerialize.h | 7 ++++-- content/xul/content/src/nsXULElement.h | 1 - .../xul/document/src/nsXULPrototypeCache.h | 3 --- dom/base/StructuredCloneTags.h | 2 -- dom/base/WindowNamedPropertiesHandler.h | 1 - dom/base/nsDOMClassInfo.h | 22 +++++++++---------- dom/base/nsDOMJSUtils.h | 1 - dom/base/nsGlobalWindow.cpp | 1 + dom/base/nsIJSEventListener.h | 1 - dom/base/nsIJSNativeInitializer.h | 1 - dom/base/nsIScriptContext.h | 1 - dom/base/nsJSEnvironment.h | 2 -- dom/base/nsPIDOMWindow.h | 1 - dom/base/nsStructuredCloneContainer.cpp | 1 + dom/base/nsStructuredCloneContainer.h | 1 - dom/base/nsWrapperCache.h | 3 ++- dom/base/nsWrapperCacheInlines.h | 1 - dom/bindings/BindingDeclarations.h | 7 ++++-- dom/bindings/BindingUtils.cpp | 1 + dom/bindings/CallbackObject.h | 2 -- dom/bindings/DOMJSClass.h | 1 - dom/bindings/DOMJSProxyHandler.h | 3 --- dom/bindings/TypedArray.h | 1 - dom/camera/ICameraControl.h | 1 - .../html/nsIDOMHTMLCanvasElement.idl | 5 ----- dom/interfaces/json/nsIJSON.idl | 6 ++++- dom/ipc/StructuredCloneUtils.h | 3 ++- dom/ipc/TabChild.h | 1 - dom/ipc/TabParent.h | 1 - dom/mobilemessage/src/MmsMessage.h | 1 - dom/mobilemessage/src/MobileMessageThread.h | 1 - dom/mobilemessage/src/SmsMessage.h | 1 - dom/plugins/base/nsJSNPRuntime.h | 1 - dom/plugins/base/nsNPAPIPlugin.h | 1 - .../ipc/PluginScriptableObjectParent.h | 1 - dom/src/events/nsJSEventListener.h | 1 - dom/src/json/nsJSON.h | 1 - dom/system/OSFileConstants.h | 1 - dom/workers/Exceptions.h | 2 -- dom/workers/File.h | 2 -- dom/workers/Location.h | 2 -- dom/workers/Principal.h | 2 -- dom/workers/RuntimeService.h | 1 - dom/workers/ScriptLoader.h | 2 -- dom/workers/Worker.h | 1 - dom/workers/WorkerPrivate.h | 1 - .../windowwatcher/src/nsWindowWatcher.h | 1 - ipc/glue/SyncChannel.h | 2 ++ ipc/testshell/TestShellParent.h | 1 - js/ipc/JavaScriptParent.cpp | 1 + js/ipc/JavaScriptParent.h | 1 - js/ipc/JavaScriptShared.h | 3 --- js/jsd/jsd.h | 5 +---- js/jsd/jsd_text.cpp | 1 + js/jsd/jsd_xpc.cpp | 1 + js/jsd/jsdebug.h | 1 - js/jsd/jshash.h | 2 +- js/public/GCAPI.h | 11 ++++------ js/src/jsfriendapi.cpp | 12 ++++++++++ js/src/jspubtd.h | 6 +++++ js/xpconnect/idl/nsIJSRuntimeService.idl | 2 ++ js/xpconnect/idl/nsIXPConnect.idl | 15 +++++++++---- js/xpconnect/idl/xpcexception.idl | 7 ++++-- js/xpconnect/loader/mozJSComponentLoader.h | 1 - js/xpconnect/loader/mozJSLoaderUtils.h | 1 - js/xpconnect/loader/mozJSSubScriptLoader.h | 1 - js/xpconnect/public/nsAXPCNativeCallContext.h | 2 +- js/xpconnect/public/nsAutoJSValHolder.h | 2 -- js/xpconnect/src/XPCComponents.cpp | 1 + js/xpconnect/src/XPCConvert.cpp | 1 + js/xpconnect/src/XPCDebug.cpp | 2 ++ js/xpconnect/src/XPCException.cpp | 1 + js/xpconnect/src/XPCInlines.h | 2 -- js/xpconnect/src/XPCJSRuntime.cpp | 2 ++ js/xpconnect/src/XPCMaps.h | 2 -- js/xpconnect/src/XPCQuickStubs.cpp | 1 + js/xpconnect/src/XPCStack.cpp | 2 ++ js/xpconnect/src/XPCThrower.cpp | 1 + js/xpconnect/src/XPCVariant.cpp | 1 + js/xpconnect/src/XPCWrappedJS.cpp | 1 + js/xpconnect/src/XPCWrappedJSClass.cpp | 1 + js/xpconnect/src/XPCWrappedNative.cpp | 1 + js/xpconnect/src/XPCWrappedNativeInfo.cpp | 1 + js/xpconnect/src/XPCWrappedNativeJSOps.cpp | 1 + js/xpconnect/src/XPCWrapper.h | 1 + js/xpconnect/src/nsScriptError.cpp | 1 + js/xpconnect/src/nsXPConnect.cpp | 1 + js/xpconnect/src/xpcprivate.h | 9 -------- js/xpconnect/src/xpcpublic.h | 4 ---- .../components/native/xpctest_params.cpp | 1 + .../tests/components/native/xpctest_private.h | 1 - js/xpconnect/wrappers/AccessCheck.h | 1 - js/xpconnect/wrappers/WaiveXrayWrapper.h | 1 - js/xpconnect/wrappers/WrapperFactory.h | 1 - js/xpconnect/wrappers/XrayWrapper.cpp | 1 + js/xpconnect/wrappers/XrayWrapper.h | 7 +++--- layout/style/nsNthIndexCache.h | 1 - .../src/peerconnection/MediaStreamList.h | 1 - netwerk/base/src/ArrayBufferInputStream.h | 2 +- netwerk/base/src/ProxyAutoConfig.h | 5 ++++- security/manager/ssl/src/nsCrypto.h | 1 - .../src/mozStorageAsyncStatementJSHelper.h | 2 +- .../src/mozStorageAsyncStatementParams.cpp | 4 +++- storage/src/mozStoragePrivateHelpers.h | 9 +++++--- storage/src/mozStorageStatementJSHelper.h | 4 ++-- storage/src/mozStorageStatementParams.cpp | 4 +++- .../ctypes/tests/jsctypes-test-errno.h | 1 - .../ctypes/tests/jsctypes-test-finalizer.h | 2 -- .../components/ctypes/tests/jsctypes-test.h | 2 +- toolkit/components/places/Helpers.h | 1 - toolkit/components/places/PlaceInfo.cpp | 1 + .../components/places/mozIAsyncHistory.idl | 4 ---- .../components/places/mozIAsyncLivemarks.idl | 4 ---- tools/profiler/GeckoProfiler.h | 1 - tools/profiler/GeckoProfilerFunc.h | 4 +++- tools/profiler/GeckoProfilerImpl.h | 1 - tools/profiler/JSObjectBuilder.h | 1 + tools/profiler/SaveProfileTask.h | 1 - xpcom/base/CycleCollectedJSRuntime.h | 2 +- xpcom/build/mozPoisonWriteMac.cpp | 1 + xpcom/reflect/xptcall/public/xptcall.h | 6 ++--- xpcom/tests/TestHarness.h | 1 - 142 files changed, 153 insertions(+), 191 deletions(-) diff --git a/caps/include/nsScriptSecurityManager.h b/caps/include/nsScriptSecurityManager.h index 8b6fa009e049..c559d93e4c68 100644 --- a/caps/include/nsScriptSecurityManager.h +++ b/caps/include/nsScriptSecurityManager.h @@ -9,8 +9,6 @@ #include "nsIScriptSecurityManager.h" #include "nsIPrincipal.h" -#include "jsapi.h" -#include "jsdbgapi.h" #include "nsIXPCSecurityManager.h" #include "nsInterfaceHashtable.h" #include "nsHashtable.h" @@ -23,6 +21,10 @@ #include +namespace JS { +template class Handle; +template class MutableHandle; +} class nsIDocShell; class nsString; class nsIClassInfo; diff --git a/content/base/public/nsIDOMFile.idl b/content/base/public/nsIDOMFile.idl index 95e8f8738733..0ef73a8e9bff 100644 --- a/content/base/public/nsIDOMFile.idl +++ b/content/base/public/nsIDOMFile.idl @@ -6,8 +6,6 @@ #include "domstubs.idl" %{C++ -#include "jsapi.h" - namespace mozilla { namespace dom { namespace indexedDB { diff --git a/content/base/public/nsIXMLHttpRequest.idl b/content/base/public/nsIXMLHttpRequest.idl index 885eff8f01b4..4a195c12f097 100644 --- a/content/base/public/nsIXMLHttpRequest.idl +++ b/content/base/public/nsIXMLHttpRequest.idl @@ -16,11 +16,6 @@ interface nsIGlobalObject; interface nsIInputStream; interface nsIDOMBlob; -%{C++ -// for jsval -#include "jsapi.h" -%} - [scriptable, builtinclass, uuid(ac97e161-9f1d-4163-adc9-e9a59e18682c)] interface nsIXMLHttpRequestEventTarget : nsIDOMEventTarget { // event handler attributes diff --git a/content/base/src/WebSocket.h b/content/base/src/WebSocket.h index 40ffe94508cc..97ec40d00130 100644 --- a/content/base/src/WebSocket.h +++ b/content/base/src/WebSocket.h @@ -22,7 +22,6 @@ // Need this for BinaryType. #include "mozilla/dom/WebSocketBinding.h" -#include "jsfriendapi.h" #include "nsISupportsUtils.h" #include "nsCOMPtr.h" #include "nsString.h" diff --git a/content/base/src/nsFrameMessageManager.h b/content/base/src/nsFrameMessageManager.h index b5b072c4b859..55b2acc62110 100644 --- a/content/base/src/nsFrameMessageManager.h +++ b/content/base/src/nsFrameMessageManager.h @@ -23,6 +23,7 @@ #include "nsThreadUtils.h" #include "nsWeakPtr.h" #include "mozilla/Attributes.h" +#include "js/RootingAPI.h" namespace mozilla { namespace dom { @@ -135,7 +136,7 @@ class MOZ_STACK_CLASS SameProcessCpowHolder : public CpowHolder bool ToObject(JSContext* aCx, JSObject** aObjp); private: - JS::RootedObject mObj; + JS::Rooted mObj; }; class nsFrameMessageManager MOZ_FINAL : public nsIContentFrameMessageManager, diff --git a/content/base/src/nsInProcessTabChildGlobal.h b/content/base/src/nsInProcessTabChildGlobal.h index 706edfdfdc14..1cb63ba2e357 100644 --- a/content/base/src/nsInProcessTabChildGlobal.h +++ b/content/base/src/nsInProcessTabChildGlobal.h @@ -15,7 +15,6 @@ #include "nsIScriptObjectPrincipal.h" #include "nsIScriptContext.h" #include "nsIClassInfo.h" -#include "jsapi.h" #include "nsIDocShell.h" #include "nsIDOMElement.h" #include "nsCOMArray.h" diff --git a/content/base/src/nsXMLHttpRequest.h b/content/base/src/nsXMLHttpRequest.h index 781979ad1f1a..27243f6ed3be 100644 --- a/content/base/src/nsXMLHttpRequest.h +++ b/content/base/src/nsXMLHttpRequest.h @@ -17,7 +17,6 @@ #include "nsIDocument.h" #include "nsIStreamListener.h" #include "nsWeakReference.h" -#include "jsapi.h" #include "nsIScriptContext.h" #include "nsIChannelEventSink.h" #include "nsIAsyncVerifyRedirectCallback.h" diff --git a/content/canvas/src/ImageData.h b/content/canvas/src/ImageData.h index abd2318ab600..f2c1a7c2dd7b 100644 --- a/content/canvas/src/ImageData.h +++ b/content/canvas/src/ImageData.h @@ -16,8 +16,6 @@ #include "nsTraceRefcnt.h" #include "xpcpublic.h" -#include "jsapi.h" - namespace mozilla { namespace dom { diff --git a/content/canvas/src/WebGLActiveInfo.h b/content/canvas/src/WebGLActiveInfo.h index aca4835ed36e..191e2228bed2 100644 --- a/content/canvas/src/WebGLActiveInfo.h +++ b/content/canvas/src/WebGLActiveInfo.h @@ -9,7 +9,12 @@ #include "WebGLTypes.h" #include "nsISupports.h" #include "nsString.h" -#include "jsapi.h" + +struct JSContext; +class JSObject; +namespace JS { +template class Handle; +} namespace mozilla { diff --git a/content/events/src/nsDOMMessageEvent.h b/content/events/src/nsDOMMessageEvent.h index 1b540dde7784..8b8dc67e1909 100644 --- a/content/events/src/nsDOMMessageEvent.h +++ b/content/events/src/nsDOMMessageEvent.h @@ -9,7 +9,6 @@ #include "nsIDOMMessageEvent.h" #include "nsDOMEvent.h" #include "nsCycleCollectionParticipant.h" -#include "jsapi.h" #include "mozilla/dom/MessageEventBinding.h" /** diff --git a/content/events/src/nsEventListenerManager.h b/content/events/src/nsEventListenerManager.h index ed8ad596c4bd..206582a59a76 100644 --- a/content/events/src/nsEventListenerManager.h +++ b/content/events/src/nsEventListenerManager.h @@ -6,7 +6,6 @@ #ifndef nsEventListenerManager_h__ #define nsEventListenerManager_h__ -#include "jsapi.h" #include "mozilla/dom/EventListenerBinding.h" #include "mozilla/MemoryReporting.h" #include "nsAutoPtr.h" diff --git a/content/html/content/src/HTMLPropertiesCollection.h b/content/html/content/src/HTMLPropertiesCollection.h index e4087b710190..957146e38bf3 100644 --- a/content/html/content/src/HTMLPropertiesCollection.h +++ b/content/html/content/src/HTMLPropertiesCollection.h @@ -19,7 +19,6 @@ #include "nsIHTMLCollection.h" #include "nsHashKeys.h" #include "nsRefPtrHashtable.h" -#include "jsapi.h" class nsGenericHTMLElement; class nsIDocument; diff --git a/content/html/content/src/nsDOMStringMap.h b/content/html/content/src/nsDOMStringMap.h index fefeedbc68f7..0ce50430d60a 100644 --- a/content/html/content/src/nsDOMStringMap.h +++ b/content/html/content/src/nsDOMStringMap.h @@ -13,7 +13,6 @@ #include "nsString.h" #include "nsWrapperCache.h" #include "nsGenericHTMLElement.h" -#include "jsfriendapi.h" namespace mozilla { class ErrorResult; diff --git a/content/html/document/src/nsHTMLDocument.h b/content/html/document/src/nsHTMLDocument.h index 625bc69caf18..9e920f1b4082 100644 --- a/content/html/document/src/nsHTMLDocument.h +++ b/content/html/document/src/nsHTMLDocument.h @@ -12,7 +12,6 @@ #include "nsIDOMHTMLDocument.h" #include "nsIDOMHTMLCollection.h" #include "nsIScriptElement.h" -#include "jsapi.h" #include "nsTArray.h" #include "pldhash.h" diff --git a/content/xbl/src/nsXBLBinding.h b/content/xbl/src/nsXBLBinding.h index add13ebf2bbd..91d12746526f 100644 --- a/content/xbl/src/nsXBLBinding.h +++ b/content/xbl/src/nsXBLBinding.h @@ -15,7 +15,6 @@ #include "nsTArray.h" #include "nsCycleCollectionParticipant.h" #include "nsISupportsImpl.h" -#include "jsapi.h" class nsXBLPrototypeBinding; class nsIContent; diff --git a/content/xbl/src/nsXBLMaybeCompiled.h b/content/xbl/src/nsXBLMaybeCompiled.h index 1342fb083f11..8a9eb863afb4 100644 --- a/content/xbl/src/nsXBLMaybeCompiled.h +++ b/content/xbl/src/nsXBLMaybeCompiled.h @@ -6,8 +6,6 @@ #ifndef nsXBLMaybeCompiled_h__ #define nsXBLMaybeCompiled_h__ -#include "js/RootingAPI.h" - /* * A union containing either a pointer representing uncompiled source or a * JSObject* representing the compiled result. The class is templated on the diff --git a/content/xbl/src/nsXBLProtoImplMember.h b/content/xbl/src/nsXBLProtoImplMember.h index 6e4f7b0357d1..611c8f2d0a8f 100644 --- a/content/xbl/src/nsXBLProtoImplMember.h +++ b/content/xbl/src/nsXBLProtoImplMember.h @@ -8,7 +8,6 @@ #include "nsIAtom.h" #include "nsString.h" -#include "jsapi.h" #include "nsString.h" #include "nsIServiceManager.h" #include "nsContentUtils.h" // For NS_CONTENT_DELETE_LIST_MEMBER. diff --git a/content/xbl/src/nsXBLProtoImplMethod.h b/content/xbl/src/nsXBLProtoImplMethod.h index f5a2152d8bce..60524649000b 100644 --- a/content/xbl/src/nsXBLProtoImplMethod.h +++ b/content/xbl/src/nsXBLProtoImplMethod.h @@ -9,7 +9,6 @@ #include "mozilla/Attributes.h" #include "nsIAtom.h" #include "nsString.h" -#include "jsapi.h" #include "nsString.h" #include "nsXBLMaybeCompiled.h" #include "nsXBLProtoImplMember.h" diff --git a/content/xbl/src/nsXBLProtoImplProperty.h b/content/xbl/src/nsXBLProtoImplProperty.h index 6491bd9f61fb..875db3e2af20 100644 --- a/content/xbl/src/nsXBLProtoImplProperty.h +++ b/content/xbl/src/nsXBLProtoImplProperty.h @@ -9,7 +9,6 @@ #include "mozilla/Attributes.h" #include "nsIAtom.h" #include "nsString.h" -#include "jsapi.h" #include "nsString.h" #include "nsXBLSerialize.h" #include "nsXBLMaybeCompiled.h" diff --git a/content/xbl/src/nsXBLPrototypeHandler.h b/content/xbl/src/nsXBLPrototypeHandler.h index 81d7717d62c3..80b93f093643 100644 --- a/content/xbl/src/nsXBLPrototypeHandler.h +++ b/content/xbl/src/nsXBLPrototypeHandler.h @@ -16,8 +16,6 @@ #include "nsIScriptGlobalObject.h" #include "nsCycleCollectionParticipant.h" -#include "js/RootingAPI.h" - class JSObject; class nsIDOMEvent; class nsIContent; @@ -28,6 +26,10 @@ class nsIObjectInputStream; class nsIObjectOutputStream; class nsXBLPrototypeBinding; +namespace JS { +template class MutableHandle; +} + namespace mozilla { namespace dom { class EventTarget; diff --git a/content/xbl/src/nsXBLSerialize.h b/content/xbl/src/nsXBLSerialize.h index 26e3e259e3f2..6032f350e802 100644 --- a/content/xbl/src/nsXBLSerialize.h +++ b/content/xbl/src/nsXBLSerialize.h @@ -6,12 +6,15 @@ #ifndef nsXBLSerialize_h__ #define nsXBLSerialize_h__ -#include "jsapi.h" - #include "nsIObjectInputStream.h" #include "nsIObjectOutputStream.h" #include "nsINameSpaceManager.h" +namespace JS { +template class Handle; +template class MutableHandle; +} + typedef uint8_t XBLBindingSerializeDetails; // A version number to ensure we don't load cached data in a different diff --git a/content/xul/content/src/nsXULElement.h b/content/xul/content/src/nsXULElement.h index 12a521f8b189..6bb51977ec2e 100644 --- a/content/xul/content/src/nsXULElement.h +++ b/content/xul/content/src/nsXULElement.h @@ -34,7 +34,6 @@ #include "nsAutoPtr.h" #include "nsStyledElement.h" #include "nsIFrameLoader.h" -#include "jspubtd.h" #include "nsFrameLoader.h" class nsIDocument; diff --git a/content/xul/document/src/nsXULPrototypeCache.h b/content/xul/document/src/nsXULPrototypeCache.h index d05a814c8c5b..52cc071d06a8 100644 --- a/content/xul/document/src/nsXULPrototypeCache.h +++ b/content/xul/document/src/nsXULPrototypeCache.h @@ -17,11 +17,8 @@ #include "nsIInputStream.h" #include "nsIStorageStream.h" -#include "jspubtd.h" - #include "mozilla/scache/StartupCache.h" - class nsCSSStyleSheet; /** diff --git a/dom/base/StructuredCloneTags.h b/dom/base/StructuredCloneTags.h index 1af0dfad66ab..4246b46ea139 100644 --- a/dom/base/StructuredCloneTags.h +++ b/dom/base/StructuredCloneTags.h @@ -5,8 +5,6 @@ #ifndef StructuredCloneTags_h__ #define StructuredCloneTags_h__ -#include "jsapi.h" - namespace mozilla { namespace dom { diff --git a/dom/base/WindowNamedPropertiesHandler.h b/dom/base/WindowNamedPropertiesHandler.h index 0aefcb8171b9..ce3b79a15145 100644 --- a/dom/base/WindowNamedPropertiesHandler.h +++ b/dom/base/WindowNamedPropertiesHandler.h @@ -7,7 +7,6 @@ #ifndef mozilla_dom_WindowNamedPropertiesHandler_h #define mozilla_dom_WindowNamedPropertiesHandler_h -#include "jsproxy.h" #include "mozilla/dom/DOMJSProxyHandler.h" namespace mozilla { diff --git a/dom/base/nsDOMClassInfo.h b/dom/base/nsDOMClassInfo.h index f11894eb0453..2f5258024f3e 100644 --- a/dom/base/nsDOMClassInfo.h +++ b/dom/base/nsDOMClassInfo.h @@ -266,7 +266,7 @@ public: NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx, JSObject *globalObj, JSObject **parentObj) MOZ_OVERRIDE; NS_IMETHOD AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, jsid id, jsval *vp, bool *_retval) MOZ_OVERRIDE; + JSObject *obj, jsid id, JS::Value *vp, bool *_retval) MOZ_OVERRIDE; virtual void PreserveWrapper(nsISupports *aNative) MOZ_OVERRIDE; @@ -355,12 +355,12 @@ protected: public: NS_IMETHOD CheckAccess(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, jsid id, uint32_t mode, - jsval *vp, bool *_retval) MOZ_OVERRIDE; + JS::Value *vp, bool *_retval) MOZ_OVERRIDE; NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx, JSObject *globalObj, JSObject **parentObj) MOZ_OVERRIDE; NS_IMETHODIMP AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, jsid id, jsval *vp, bool *_retval); + JSObject *obj, jsid id, JS::Value *vp, bool *_retval); static nsIClassInfo *doCreate(nsDOMClassInfoData* aData) { @@ -419,7 +419,7 @@ protected: public: NS_IMETHOD GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, jsid id, jsval *vp, bool *_retval) MOZ_OVERRIDE; + JSObject *obj, jsid id, JS::Value *vp, bool *_retval) MOZ_OVERRIDE; private: // Not implemented, nothing should create an instance of this class. @@ -443,7 +443,7 @@ public: static bool DocumentAllNewResolve(JSContext *cx, JS::Handle obj, JS::Handle id, unsigned flags, JS::MutableHandle objp); static void ReleaseDocument(JSFreeOp *fop, JSObject *obj); - static bool CallToGetPropMapper(JSContext *cx, unsigned argc, jsval *vp); + static bool CallToGetPropMapper(JSContext *cx, unsigned argc, JS::Value *vp); }; @@ -465,7 +465,7 @@ protected: public: NS_IMETHOD GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, jsid id, jsval *vp, bool *_retval) MOZ_OVERRIDE; + JSObject *obj, jsid id, JS::Value *vp, bool *_retval) MOZ_OVERRIDE; }; @@ -489,7 +489,7 @@ public: NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx, JSObject *globalObj, JSObject **parentObj) MOZ_OVERRIDE; NS_IMETHOD GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, jsid id, jsval *vp, bool *_retval) MOZ_OVERRIDE; + JSObject *obj, jsid id, JS::Value *vp, bool *_retval) MOZ_OVERRIDE; static nsIClassInfo *doCreate(nsDOMClassInfoData* aData) { @@ -611,13 +611,13 @@ protected: JSObject *obj, jsid id, uint32_t flags, JSObject **objp, bool *_retval) MOZ_OVERRIDE; NS_IMETHOD SetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, jsid id, jsval *vp, bool *_retval) MOZ_OVERRIDE; + JSObject *obj, jsid id, JS::Value *vp, bool *_retval) MOZ_OVERRIDE; NS_IMETHOD GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, jsid id, jsval *vp, bool *_retval) MOZ_OVERRIDE; + JSObject *obj, jsid id, JS::Value *vp, bool *_retval) MOZ_OVERRIDE; NS_IMETHOD DelProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, jsid id, bool *_retval) MOZ_OVERRIDE; NS_IMETHOD NewEnumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, uint32_t enum_op, jsval *statep, + JSObject *obj, uint32_t enum_op, JS::Value *statep, jsid *idp, bool *_retval) MOZ_OVERRIDE; public: @@ -676,7 +676,7 @@ public: JSObject *obj, const JS::CallArgs &args, bool *_retval) MOZ_OVERRIDE; NS_IMETHOD HasInstance(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, const jsval &val, bool *bp, + JSObject *obj, const JS::Value &val, bool *bp, bool *_retval); static nsIClassInfo *doCreate(nsDOMClassInfoData* aData) diff --git a/dom/base/nsDOMJSUtils.h b/dom/base/nsDOMJSUtils.h index c8d7f4b635c9..1d0a62207442 100644 --- a/dom/base/nsDOMJSUtils.h +++ b/dom/base/nsDOMJSUtils.h @@ -6,7 +6,6 @@ #ifndef nsDOMJSUtils_h__ #define nsDOMJSUtils_h__ -#include "jsapi.h" #include "nsIScriptContext.h" class nsIJSArgArray; diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 09546d946a25..b9bdda57e2ca 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -183,6 +183,7 @@ #endif #include "prlog.h" #include "prenv.h" +#include "prprf.h" #include "mozilla/dom/indexedDB/IDBFactory.h" #include "mozilla/dom/quota/QuotaManager.h" diff --git a/dom/base/nsIJSEventListener.h b/dom/base/nsIJSEventListener.h index c7395d9cb960..23e35ca39d91 100644 --- a/dom/base/nsIJSEventListener.h +++ b/dom/base/nsIJSEventListener.h @@ -7,7 +7,6 @@ #define nsIJSEventListener_h__ #include "nsIScriptContext.h" -#include "jsapi.h" #include "xpcpublic.h" #include "nsIDOMEventListener.h" #include "nsIAtom.h" diff --git a/dom/base/nsIJSNativeInitializer.h b/dom/base/nsIJSNativeInitializer.h index ef25f6f752ef..4ecdb6cbf775 100644 --- a/dom/base/nsIJSNativeInitializer.h +++ b/dom/base/nsIJSNativeInitializer.h @@ -7,7 +7,6 @@ #define nsIJSNativeInitializer_h__ #include "nsISupports.h" -#include "jsapi.h" #define NS_IJSNATIVEINITIALIZER_IID \ { 0xdb48eee5, 0x89a4, 0x4f18, \ diff --git a/dom/base/nsIScriptContext.h b/dom/base/nsIScriptContext.h index a95c78090218..5c72e4781526 100644 --- a/dom/base/nsIScriptContext.h +++ b/dom/base/nsIScriptContext.h @@ -11,7 +11,6 @@ #include "nsISupports.h" #include "nsCOMPtr.h" #include "nsIProgrammingLanguage.h" -#include "jsfriendapi.h" #include "jspubtd.h" #include "js/GCAPI.h" diff --git a/dom/base/nsJSEnvironment.h b/dom/base/nsJSEnvironment.h index c54e0e07e228..c9803e7f4da5 100644 --- a/dom/base/nsJSEnvironment.h +++ b/dom/base/nsJSEnvironment.h @@ -8,8 +8,6 @@ #include "nsIScriptContext.h" #include "nsIScriptGlobalObject.h" #include "nsCOMPtr.h" -#include "jsapi.h" -#include "jsfriendapi.h" #include "nsIObserver.h" #include "prtime.h" #include "nsCycleCollectionParticipant.h" diff --git a/dom/base/nsPIDOMWindow.h b/dom/base/nsPIDOMWindow.h index 78c0079492be..428db39cbcd1 100644 --- a/dom/base/nsPIDOMWindow.h +++ b/dom/base/nsPIDOMWindow.h @@ -19,7 +19,6 @@ #include "nsTArray.h" #include "nsIURI.h" #include "mozilla/dom/EventTarget.h" -#include "js/RootingAPI.h" #define DOM_WINDOW_DESTROYED_TOPIC "dom-window-destroyed" #define DOM_WINDOW_FROZEN_TOPIC "dom-window-frozen" diff --git a/dom/base/nsStructuredCloneContainer.cpp b/dom/base/nsStructuredCloneContainer.cpp index 9eb8bc753b43..9f9bee55f309 100644 --- a/dom/base/nsStructuredCloneContainer.cpp +++ b/dom/base/nsStructuredCloneContainer.cpp @@ -13,6 +13,7 @@ #include "nsIXPConnect.h" #include "nsServiceManagerUtils.h" #include "nsContentUtils.h" +#include "jsapi.h" #include "mozilla/Base64.h" diff --git a/dom/base/nsStructuredCloneContainer.h b/dom/base/nsStructuredCloneContainer.h index 864e5716e038..7942bff40d2d 100644 --- a/dom/base/nsStructuredCloneContainer.h +++ b/dom/base/nsStructuredCloneContainer.h @@ -9,7 +9,6 @@ #define nsStructuredCloneContainer_h__ #include "nsIStructuredCloneContainer.h" -#include "jsapi.h" #include "mozilla/Attributes.h" #define NS_STRUCTUREDCLONECONTAINER_CONTRACTID \ diff --git a/dom/base/nsWrapperCache.h b/dom/base/nsWrapperCache.h index 21963d71d88b..7708d562e9ab 100644 --- a/dom/base/nsWrapperCache.h +++ b/dom/base/nsWrapperCache.h @@ -8,7 +8,8 @@ #include "nsCycleCollectionParticipant.h" #include "mozilla/Assertions.h" -#include "js/Value.h" +#include "js/Value.h" // must come before js/RootingAPI.h +#include "js/RootingAPI.h" struct JSTracer; class JSObject; diff --git a/dom/base/nsWrapperCacheInlines.h b/dom/base/nsWrapperCacheInlines.h index 97472092718d..bdf413ee94a9 100644 --- a/dom/base/nsWrapperCacheInlines.h +++ b/dom/base/nsWrapperCacheInlines.h @@ -8,7 +8,6 @@ #include "nsWrapperCache.h" #include "xpcpublic.h" -#include "jsapi.h" inline JSObject* nsWrapperCache::GetWrapper() const diff --git a/dom/bindings/BindingDeclarations.h b/dom/bindings/BindingDeclarations.h index d799db3fecf0..32769ddee6a6 100644 --- a/dom/bindings/BindingDeclarations.h +++ b/dom/bindings/BindingDeclarations.h @@ -14,7 +14,8 @@ #define mozilla_dom_BindingDeclarations_h__ #include "nsStringGlue.h" -#include "jsapi.h" +#include "js/Value.h" +#include "js/RootingAPI.h" #include "mozilla/Util.h" #include "nsCOMPtr.h" #include "nsDOMString.h" @@ -22,6 +23,8 @@ #include "nsTArray.h" #include "nsAutoPtr.h" // for nsRefPtr member variables +struct JSContext; +class JSObject; class nsWrapperCache; // nsGlobalWindow implements nsWrapperCache, but doesn't always use it. Don't @@ -72,7 +75,7 @@ public: } private: - JS::RootedObject mGlobalJSObject; + JS::Rooted mGlobalJSObject; nsISupports* mGlobalObject; nsCOMPtr mGlobalObjectRef; }; diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index 50a2f4a1247d..907ecf6e9a18 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -24,6 +24,7 @@ #include "XPCQuickStubs.h" #include "XrayWrapper.h" #include "nsPrintfCString.h" +#include "prprf.h" #include "mozilla/dom/HTMLObjectElement.h" #include "mozilla/dom/HTMLObjectElementBinding.h" diff --git a/dom/bindings/CallbackObject.h b/dom/bindings/CallbackObject.h index b9c85f29b67a..1e8be8180127 100644 --- a/dom/bindings/CallbackObject.h +++ b/dom/bindings/CallbackObject.h @@ -20,7 +20,6 @@ #include "nsISupports.h" #include "nsISupportsImpl.h" #include "nsCycleCollectionParticipant.h" -#include "jsapi.h" #include "jswrapper.h" #include "mozilla/Assertions.h" #include "mozilla/ErrorResult.h" @@ -31,7 +30,6 @@ #include "nsJSEnvironment.h" #include "xpcpublic.h" #include "nsLayoutStatics.h" -#include "js/RootingAPI.h" namespace mozilla { namespace dom { diff --git a/dom/bindings/DOMJSClass.h b/dom/bindings/DOMJSClass.h index 0e7bc44e540c..c4641bc93190 100644 --- a/dom/bindings/DOMJSClass.h +++ b/dom/bindings/DOMJSClass.h @@ -6,7 +6,6 @@ #ifndef mozilla_dom_DOMJSClass_h #define mozilla_dom_DOMJSClass_h -#include "jsapi.h" #include "jsfriendapi.h" #include "mozilla/Assertions.h" diff --git a/dom/bindings/DOMJSProxyHandler.h b/dom/bindings/DOMJSProxyHandler.h index 6dd18dae3c67..1b61959fb3be 100644 --- a/dom/bindings/DOMJSProxyHandler.h +++ b/dom/bindings/DOMJSProxyHandler.h @@ -9,9 +9,6 @@ #include "mozilla/Attributes.h" #include "mozilla/Likely.h" -#include "jsapi.h" -#include "jsfriendapi.h" -#include "jsproxy.h" #include "xpcpublic.h" #include "nsStringGlue.h" diff --git a/dom/bindings/TypedArray.h b/dom/bindings/TypedArray.h index 89e930667ace..25dec4202e72 100644 --- a/dom/bindings/TypedArray.h +++ b/dom/bindings/TypedArray.h @@ -8,7 +8,6 @@ #define mozilla_dom_TypedArray_h #include "jsfriendapi.h" -#include "js/RootingAPI.h" #include "jsapi.h" #include "mozilla/dom/BindingDeclarations.h" diff --git a/dom/camera/ICameraControl.h b/dom/camera/ICameraControl.h index 498226e728a7..2af0a9510f38 100644 --- a/dom/camera/ICameraControl.h +++ b/dom/camera/ICameraControl.h @@ -5,7 +5,6 @@ #ifndef DOM_CAMERA_ICAMERACONTROL_H #define DOM_CAMERA_ICAMERACONTROL_H -#include "jsapi.h" #include "nsIFile.h" #include "nsIDOMCameraManager.h" #include "DictionaryHelpers.h" diff --git a/dom/interfaces/html/nsIDOMHTMLCanvasElement.idl b/dom/interfaces/html/nsIDOMHTMLCanvasElement.idl index c9f40ea36ef9..ffd95f6fdb02 100644 --- a/dom/interfaces/html/nsIDOMHTMLCanvasElement.idl +++ b/dom/interfaces/html/nsIDOMHTMLCanvasElement.idl @@ -5,11 +5,6 @@ #include "nsIDOMHTMLElement.idl" -%{C++ -// for jsval -#include "jsapi.h" -%} - /** * The nsIDOMHTMLCanvasElement interface is the interface to a HTML * element. diff --git a/dom/interfaces/json/nsIJSON.idl b/dom/interfaces/json/nsIJSON.idl index a3dd0aba7bb3..41ac516e76a8 100644 --- a/dom/interfaces/json/nsIJSON.idl +++ b/dom/interfaces/json/nsIJSON.idl @@ -9,9 +9,13 @@ interface nsIInputStream; interface nsIOutputStream; interface nsIScriptGlobalObject; -[ptr] native JSValPtr(jsval); +[ptr] native JSValPtr(JS::Value); [ptr] native JSContext(JSContext); +%{C++ +namespace JS { class Value; } +%} + /** * Don't use this! Use JSON.parse and JSON.stringify directly. */ diff --git a/dom/ipc/StructuredCloneUtils.h b/dom/ipc/StructuredCloneUtils.h index e4b0618b443d..9c285775b2db 100644 --- a/dom/ipc/StructuredCloneUtils.h +++ b/dom/ipc/StructuredCloneUtils.h @@ -7,11 +7,12 @@ #ifndef mozilla_dom_StructuredCloneUtils_h #define mozilla_dom_StructuredCloneUtils_h -#include "jsapi.h" #include "nsCOMPtr.h" #include "nsTArray.h" #include "nsIDOMFile.h" +#include "jsapi.h" + namespace mozilla { struct SerializedStructuredCloneBuffer; diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h index d3bcf9719288..17339ec9d548 100644 --- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -22,7 +22,6 @@ #include "nsIDOMEventListener.h" #include "nsIInterfaceRequestor.h" #include "nsIWindowProvider.h" -#include "jsapi.h" #include "nsIDOMWindow.h" #include "nsIDocShell.h" #include "nsIDocShellTreeItem.h" diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index 002bbe30aa13..d31662f907be 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -9,7 +9,6 @@ #include "base/basictypes.h" -#include "jsapi.h" #include "mozilla/dom/ContentParent.h" #include "mozilla/dom/PBrowserParent.h" #include "mozilla/dom/PContentDialogParent.h" diff --git a/dom/mobilemessage/src/MmsMessage.h b/dom/mobilemessage/src/MmsMessage.h index 86f5d5e7440c..d41cb4d9af14 100644 --- a/dom/mobilemessage/src/MmsMessage.h +++ b/dom/mobilemessage/src/MmsMessage.h @@ -8,7 +8,6 @@ #include "nsIDOMMozMmsMessage.h" #include "nsString.h" -#include "jspubtd.h" #include "mozilla/dom/mobilemessage/Types.h" #include "mozilla/Attributes.h" #include "DictionaryHelpers.h" diff --git a/dom/mobilemessage/src/MobileMessageThread.h b/dom/mobilemessage/src/MobileMessageThread.h index 56df350ded50..0ac071688986 100644 --- a/dom/mobilemessage/src/MobileMessageThread.h +++ b/dom/mobilemessage/src/MobileMessageThread.h @@ -10,7 +10,6 @@ #include "mozilla/dom/mobilemessage/SmsTypes.h" #include "nsIDOMMozMobileMessageThread.h" #include "nsString.h" -#include "jspubtd.h" namespace mozilla { namespace dom { diff --git a/dom/mobilemessage/src/SmsMessage.h b/dom/mobilemessage/src/SmsMessage.h index e00815f9dbea..b05b20a3a7a0 100644 --- a/dom/mobilemessage/src/SmsMessage.h +++ b/dom/mobilemessage/src/SmsMessage.h @@ -9,7 +9,6 @@ #include "mozilla/dom/mobilemessage/SmsTypes.h" #include "nsIDOMMozSmsMessage.h" #include "nsString.h" -#include "jspubtd.h" #include "mozilla/dom/mobilemessage/Types.h" #include "mozilla/Attributes.h" diff --git a/dom/plugins/base/nsJSNPRuntime.h b/dom/plugins/base/nsJSNPRuntime.h index 2542afab6b52..b14f171646a2 100644 --- a/dom/plugins/base/nsJSNPRuntime.h +++ b/dom/plugins/base/nsJSNPRuntime.h @@ -7,7 +7,6 @@ #define nsJSNPRuntime_h_ #include "nscore.h" -#include "jsapi.h" #include "npapi.h" #include "npruntime.h" #include "pldhash.h" diff --git a/dom/plugins/base/nsNPAPIPlugin.h b/dom/plugins/base/nsNPAPIPlugin.h index 37587f77eadb..a806dbf6d221 100644 --- a/dom/plugins/base/nsNPAPIPlugin.h +++ b/dom/plugins/base/nsNPAPIPlugin.h @@ -10,7 +10,6 @@ #include "npfunctions.h" #include "nsPluginHost.h" -#include "jsapi.h" #include "nsCxPusher.h" #include "mozilla/PluginLibrary.h" diff --git a/dom/plugins/ipc/PluginScriptableObjectParent.h b/dom/plugins/ipc/PluginScriptableObjectParent.h index 16d6881f6f20..5a05a90cd855 100644 --- a/dom/plugins/ipc/PluginScriptableObjectParent.h +++ b/dom/plugins/ipc/PluginScriptableObjectParent.h @@ -9,7 +9,6 @@ #include "mozilla/plugins/PPluginScriptableObjectParent.h" -#include "jsapi.h" #include "npfunctions.h" #include "npruntime.h" diff --git a/dom/src/events/nsJSEventListener.h b/dom/src/events/nsJSEventListener.h index bc8e83e5763e..77f833333298 100644 --- a/dom/src/events/nsJSEventListener.h +++ b/dom/src/events/nsJSEventListener.h @@ -11,7 +11,6 @@ #include "nsIDOMKeyEvent.h" #include "nsIJSEventListener.h" #include "nsIDOMEventListener.h" -#include "jsapi.h" #include "nsCOMPtr.h" #include "nsIAtom.h" #include "nsIScriptContext.h" diff --git a/dom/src/json/nsJSON.h b/dom/src/json/nsJSON.h index dd2785304478..c43980169785 100644 --- a/dom/src/json/nsJSON.h +++ b/dom/src/json/nsJSON.h @@ -6,7 +6,6 @@ #ifndef nsJSON_h__ #define nsJSON_h__ -#include "jsapi.h" #include "nsIJSON.h" #include "nsString.h" #include "nsCOMPtr.h" diff --git a/dom/system/OSFileConstants.h b/dom/system/OSFileConstants.h index 1be0419c0940..bcd1dddf4220 100644 --- a/dom/system/OSFileConstants.h +++ b/dom/system/OSFileConstants.h @@ -5,7 +5,6 @@ #ifndef mozilla_osfileconstants_h__ #define mozilla_osfileconstants_h__ -#include "jspubtd.h" #include "nsIOSFileConstantsService.h" #include "mozilla/Attributes.h" diff --git a/dom/workers/Exceptions.h b/dom/workers/Exceptions.h index 4550decd7134..1d1555161444 100644 --- a/dom/workers/Exceptions.h +++ b/dom/workers/Exceptions.h @@ -9,8 +9,6 @@ #include "Workers.h" -#include "jspubtd.h" - // DOMException Codes. #define INDEX_SIZE_ERR 1 #define DOMSTRING_SIZE_ERR 2 diff --git a/dom/workers/File.h b/dom/workers/File.h index 772bad168cd1..66695ca4722a 100644 --- a/dom/workers/File.h +++ b/dom/workers/File.h @@ -9,8 +9,6 @@ #include "Workers.h" -#include "jspubtd.h" - class nsIDOMFile; class nsIDOMBlob; diff --git a/dom/workers/Location.h b/dom/workers/Location.h index 7d8ff941a640..5365038b7b98 100644 --- a/dom/workers/Location.h +++ b/dom/workers/Location.h @@ -10,8 +10,6 @@ #include "DOMBindingBase.h" #include "WorkerPrivate.h" -#include "jspubtd.h" - BEGIN_WORKERS_NAMESPACE class WorkerLocation MOZ_FINAL : public DOMBindingBase diff --git a/dom/workers/Principal.h b/dom/workers/Principal.h index 92b4ad79e251..76e32f208f26 100644 --- a/dom/workers/Principal.h +++ b/dom/workers/Principal.h @@ -8,8 +8,6 @@ #include "Workers.h" -#include "jspubtd.h" - BEGIN_WORKERS_NAMESPACE JSPrincipals* diff --git a/dom/workers/RuntimeService.h b/dom/workers/RuntimeService.h index 5bf62395d5af..df6adf355600 100644 --- a/dom/workers/RuntimeService.h +++ b/dom/workers/RuntimeService.h @@ -11,7 +11,6 @@ #include "nsIObserver.h" -#include "jsapi.h" #include "mozilla/Attributes.h" #include "mozilla/Mutex.h" #include "mozilla/TimeStamp.h" diff --git a/dom/workers/ScriptLoader.h b/dom/workers/ScriptLoader.h index cb79abea2510..8c34b25f0727 100644 --- a/dom/workers/ScriptLoader.h +++ b/dom/workers/ScriptLoader.h @@ -8,8 +8,6 @@ #include "Workers.h" -#include "jsapi.h" - class nsIPrincipal; class nsIURI; class nsIDocument; diff --git a/dom/workers/Worker.h b/dom/workers/Worker.h index dad716084e99..ee7578acd3d1 100644 --- a/dom/workers/Worker.h +++ b/dom/workers/Worker.h @@ -8,7 +8,6 @@ #include "Workers.h" -#include "jspubtd.h" #include "mozilla/dom/DOMJSClass.h" BEGIN_WORKERS_NAMESPACE diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h index e04bd5cf3a25..c78d4d7b1585 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -15,7 +15,6 @@ #include "nsIThreadInternal.h" #include "nsPIDOMWindow.h" -#include "jsapi.h" #include "mozilla/Assertions.h" #include "mozilla/CondVar.h" #include "mozilla/Mutex.h" diff --git a/embedding/components/windowwatcher/src/nsWindowWatcher.h b/embedding/components/windowwatcher/src/nsWindowWatcher.h index bc3f85516232..78ee962dce86 100644 --- a/embedding/components/windowwatcher/src/nsWindowWatcher.h +++ b/embedding/components/windowwatcher/src/nsWindowWatcher.h @@ -11,7 +11,6 @@ {0xa21bfa01, 0xf349, 0x4394, {0xa8, 0x4c, 0x8d, 0xe5, 0xcf, 0x7, 0x37, 0xd0}} #include "nsCOMPtr.h" -#include "jspubtd.h" #include "mozilla/Mutex.h" #include "nsIWindowCreator.h" // for stupid compilers #include "nsIWindowWatcher.h" diff --git a/ipc/glue/SyncChannel.h b/ipc/glue/SyncChannel.h index dd1ad86bf7de..1f725620d99d 100644 --- a/ipc/glue/SyncChannel.h +++ b/ipc/glue/SyncChannel.h @@ -10,6 +10,8 @@ #include "mozilla/ipc/AsyncChannel.h" +#include + namespace mozilla { namespace ipc { //----------------------------------------------------------------------------- diff --git a/ipc/testshell/TestShellParent.h b/ipc/testshell/TestShellParent.h index 12be80a6b674..c975dace56de 100644 --- a/ipc/testshell/TestShellParent.h +++ b/ipc/testshell/TestShellParent.h @@ -11,7 +11,6 @@ #include "mozilla/ipc/PTestShellParent.h" #include "mozilla/ipc/PTestShellCommandParent.h" -#include "jsapi.h" #include "nsAutoJSValHolder.h" #include "nsStringGlue.h" diff --git a/js/ipc/JavaScriptParent.cpp b/js/ipc/JavaScriptParent.cpp index 7ec8c757e513..b5ce31d91a29 100644 --- a/js/ipc/JavaScriptParent.cpp +++ b/js/ipc/JavaScriptParent.cpp @@ -10,6 +10,7 @@ #include "nsJSUtils.h" #include "jsfriendapi.h" #include "jsproxy.h" +#include "jswrapper.h" #include "HeapAPI.h" #include "xpcprivate.h" #include "mozilla/Casting.h" diff --git a/js/ipc/JavaScriptParent.h b/js/ipc/JavaScriptParent.h index afd18b6652e5..041149e3dde8 100644 --- a/js/ipc/JavaScriptParent.h +++ b/js/ipc/JavaScriptParent.h @@ -10,7 +10,6 @@ #include "JavaScriptShared.h" #include "mozilla/jsipc/PJavaScriptParent.h" -#include "jsclass.h" #ifdef XP_WIN #undef GetClassName diff --git a/js/ipc/JavaScriptShared.h b/js/ipc/JavaScriptShared.h index 14711d7a7f1a..b84141767acd 100644 --- a/js/ipc/JavaScriptShared.h +++ b/js/ipc/JavaScriptShared.h @@ -8,9 +8,6 @@ #ifndef mozilla_jsipc_JavaScriptShared_h__ #define mozilla_jsipc_JavaScriptShared_h__ -#include "jsapi.h" -#include "jspubtd.h" -#include "js/HashTable.h" #include "mozilla/dom/DOMTypes.h" #include "mozilla/jsipc/PJavaScript.h" #include "nsJSUtils.h" diff --git a/js/jsd/jsd.h b/js/jsd/jsd.h index 46e451ec0cd3..acad7972944b 100644 --- a/js/jsd/jsd.h +++ b/js/jsd/jsd.h @@ -29,12 +29,9 @@ #define JSD_USE_NSPR_LOCKS 1 #endif /* MOZILLA_CLIENT */ -#include "jstypes.h" -#include "jsprf.h" -#include "jshash.h" /* Added by JSIFY */ +#include "jshash.h" #include "jsclist.h" #include "jsdebug.h" -#include "jsapi.h" #include "jsdbgapi.h" #include "jsd_lock.h" diff --git a/js/jsd/jsd_text.cpp b/js/jsd/jsd_text.cpp index d4cfa17008d3..df8ec12d7637 100644 --- a/js/jsd/jsd_text.cpp +++ b/js/jsd/jsd_text.cpp @@ -10,6 +10,7 @@ #include #include "jsd.h" +#include "jsprf.h" #ifdef DEBUG void JSD_ASSERT_VALID_SOURCE_TEXT(JSDSourceText* jsdsrc) diff --git a/js/jsd/jsd_xpc.cpp b/js/jsd/jsd_xpc.cpp index 1a3ea5f4b831..a28b38b50b78 100644 --- a/js/jsd/jsd_xpc.cpp +++ b/js/jsd/jsd_xpc.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "jsdbgapi.h" +#include "jsfriendapi.h" #include "jsd_xpc.h" #include "js/GCAPI.h" diff --git a/js/jsd/jsdebug.h b/js/jsd/jsdebug.h index 2a36d58c35ee..4d9c8c9496be 100644 --- a/js/jsd/jsdebug.h +++ b/js/jsd/jsdebug.h @@ -12,7 +12,6 @@ #define jsdebug_h___ #include "jsapi.h" -#include "jsdbgapi.h" extern "C" { diff --git a/js/jsd/jshash.h b/js/jsd/jshash.h index b83bbf71c3ab..a0baa17683c8 100644 --- a/js/jsd/jshash.h +++ b/js/jsd/jshash.h @@ -11,8 +11,8 @@ * API to portable hash table code. */ #include +#include #include -#include "jstypes.h" extern "C" { diff --git a/js/public/GCAPI.h b/js/public/GCAPI.h index 9c2dc433f4ae..63e6c772c6b1 100644 --- a/js/public/GCAPI.h +++ b/js/public/GCAPI.h @@ -9,6 +9,7 @@ #include "js/HeapAPI.h" #include "js/RootingAPI.h" +#include "js/Value.h" namespace JS { @@ -207,7 +208,7 @@ PokeGC(JSRuntime *rt); extern JS_FRIEND_API(bool) WasIncrementalGC(JSRuntime *rt); -class ObjectPtr +class JS_PUBLIC_API(ObjectPtr) { Heap value; @@ -233,9 +234,7 @@ class ObjectPtr IncrementalObjectBarrier(value); } - bool isAboutToBeFinalized() { - return JS_IsAboutToBeFinalized(&value); - } + bool isAboutToBeFinalized(); ObjectPtr &operator=(JSObject *obj) { IncrementalObjectBarrier(value); @@ -243,9 +242,7 @@ class ObjectPtr return *this; } - void trace(JSTracer *trc, const char *name) { - JS_CallHeapObjectTracer(trc, &value, name); - } + void trace(JSTracer *trc, const char *name); JSObject &operator*() const { return *value; } JSObject *operator->() const { return value; } diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 3e550c56fb03..a85fa6355664 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -958,6 +958,18 @@ js::GetAnyCompartmentInZone(JS::Zone *zone) return comp.get(); } +bool +JS::ObjectPtr::isAboutToBeFinalized() +{ + return JS_IsAboutToBeFinalized(&value); +} + +void +JS::ObjectPtr::trace(JSTracer *trc, const char *name) +{ + JS_CallHeapObjectTracer(trc, &value, name); +} + JS_FRIEND_API(JSObject *) js::GetTestingFunctions(JSContext *cx) { diff --git a/js/src/jspubtd.h b/js/src/jspubtd.h index 98470053b6b0..dc7612d1e0f1 100644 --- a/js/src/jspubtd.h +++ b/js/src/jspubtd.h @@ -29,11 +29,17 @@ namespace JS { */ class Value; +class AutoIdVector; +class CallArgs; + template class Rooted; class JS_PUBLIC_API(AutoGCRooter); +class JS_PUBLIC_API(CompileOptions); +class JS_PUBLIC_API(CompartmentOptions); + struct Zone; } /* namespace JS */ diff --git a/js/xpconnect/idl/nsIJSRuntimeService.idl b/js/xpconnect/idl/nsIJSRuntimeService.idl index bc841ae857cd..c6781274e41c 100644 --- a/js/xpconnect/idl/nsIJSRuntimeService.idl +++ b/js/xpconnect/idl/nsIJSRuntimeService.idl @@ -13,6 +13,8 @@ native xpcContextCallback(xpcContextCallback); %{C++ +#include "jsapi.h" // for JSGCStatus + typedef void (* xpcGCCallback)(JSGCStatus status); diff --git a/js/xpconnect/idl/nsIXPConnect.idl b/js/xpconnect/idl/nsIXPConnect.idl index 400d233f6f76..aef0342600ac 100644 --- a/js/xpconnect/idl/nsIXPConnect.idl +++ b/js/xpconnect/idl/nsIXPConnect.idl @@ -23,19 +23,26 @@ #include "xptinfo.h" #include "nsAXPCNativeCallContext.h" +struct JSFreeOp; + +namespace JS { +template class Handle; +template class MutableHandle; +} + class nsWrapperCache; %} /***************************************************************************/ -// NB: jsval and jsid are declared in nsIVariant.idl +// NB: jsval and jsid are declared in nsrootidl.idl [ptr] native JSContextPtr(JSContext); [ptr] native JSClassPtr(JSClass); [ptr] native JSFreeOpPtr(JSFreeOp); [ptr] native JSObjectPtr(JSObject); -[ptr] native JSValPtr(jsval); -[ptr] native JSValConstPtr(const jsval); +[ptr] native JSValPtr(JS::Value); +[ptr] native JSValConstPtr(const JS::Value); native JSPropertyOp(JSPropertyOp); native JSEqualityOp(JSEqualityOp); [ptr] native JSScriptPtr(JSScript); @@ -46,7 +53,7 @@ class nsWrapperCache; [ptr] native nsWrapperCachePtr(nsWrapperCache); [ref] native JSCompartmentOptions(JS::CompartmentOptions); [ref] native JSCallArgsRef(const JS::CallArgs); - native JSHandleId(JS::HandleId); + native JSHandleId(JS::Handle); /***************************************************************************/ diff --git a/js/xpconnect/idl/xpcexception.idl b/js/xpconnect/idl/xpcexception.idl index d9b1180a37f0..4806f18d39ee 100644 --- a/js/xpconnect/idl/xpcexception.idl +++ b/js/xpconnect/idl/xpcexception.idl @@ -8,11 +8,14 @@ #include "nsIException.idl" %{ C++ -#include "jsapi.h" +struct JSContext; +namespace JS { +class Value; +} %} [ptr] native xpcexJSContextPtr(JSContext); - native xpcexJSVal(jsval); + native xpcexJSVal(JS::Value); [scriptable, uuid(cac29630-7bf2-4e22-811b-46855a7d5af0)] interface nsIXPCException : nsIException diff --git a/js/xpconnect/loader/mozJSComponentLoader.h b/js/xpconnect/loader/mozJSComponentLoader.h index 324d8b42eed6..10bf223e9e81 100644 --- a/js/xpconnect/loader/mozJSComponentLoader.h +++ b/js/xpconnect/loader/mozJSComponentLoader.h @@ -5,7 +5,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "plhash.h" -#include "jsapi.h" #include "mozilla/ModuleLoader.h" #include "nsIJSRuntimeService.h" #include "nsISupports.h" diff --git a/js/xpconnect/loader/mozJSLoaderUtils.h b/js/xpconnect/loader/mozJSLoaderUtils.h index dfabddbf1c80..05e58ec3cb8b 100644 --- a/js/xpconnect/loader/mozJSLoaderUtils.h +++ b/js/xpconnect/loader/mozJSLoaderUtils.h @@ -8,7 +8,6 @@ #define mozJSLoaderUtils_h #include "nsString.h" -#include "jsapi.h" class nsIURI; namespace mozilla { diff --git a/js/xpconnect/loader/mozJSSubScriptLoader.h b/js/xpconnect/loader/mozJSSubScriptLoader.h index 58476754d567..1167859c3dde 100644 --- a/js/xpconnect/loader/mozJSSubScriptLoader.h +++ b/js/xpconnect/loader/mozJSSubScriptLoader.h @@ -4,7 +4,6 @@ * 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 "jsapi.h" #include "nsCOMPtr.h" #include "mozIJSSubScriptLoader.h" #include "nsIScriptSecurityManager.h" diff --git a/js/xpconnect/public/nsAXPCNativeCallContext.h b/js/xpconnect/public/nsAXPCNativeCallContext.h index 1ba0ce4d0295..ca6ff44fcc53 100644 --- a/js/xpconnect/public/nsAXPCNativeCallContext.h +++ b/js/xpconnect/public/nsAXPCNativeCallContext.h @@ -22,7 +22,7 @@ public: NS_IMETHOD GetCalleeWrapper(nsIXPConnectWrappedNative **aResult) = 0; NS_IMETHOD GetJSContext(JSContext **aResult) = 0; NS_IMETHOD GetArgc(uint32_t *aResult) = 0; - NS_IMETHOD GetArgvPtr(jsval **aResult) = 0; + NS_IMETHOD GetArgvPtr(JS::Value **aResult) = 0; // Methods added since mozilla 0.6.... diff --git a/js/xpconnect/public/nsAutoJSValHolder.h b/js/xpconnect/public/nsAutoJSValHolder.h index aedb1a079b69..54fe8f3be880 100644 --- a/js/xpconnect/public/nsAutoJSValHolder.h +++ b/js/xpconnect/public/nsAutoJSValHolder.h @@ -6,8 +6,6 @@ #ifndef __NSAUTOJSVALHOLDER_H__ #define __NSAUTOJSVALHOLDER_H__ -#include "jsapi.h" - #include "nsDebug.h" /** diff --git a/js/xpconnect/src/XPCComponents.cpp b/js/xpconnect/src/XPCComponents.cpp index a8996ce7bb75..1ae811156239 100644 --- a/js/xpconnect/src/XPCComponents.cpp +++ b/js/xpconnect/src/XPCComponents.cpp @@ -17,6 +17,7 @@ #include "nsIDOMWindow.h" #include "XPCJSWeakReference.h" #include "XPCWrapper.h" +#include "jsdbgapi.h" #include "jsproxy.h" #include "WrapperFactory.h" #include "XrayWrapper.h" diff --git a/js/xpconnect/src/XPCConvert.cpp b/js/xpconnect/src/XPCConvert.cpp index 33ab502d9ff7..6544221c4195 100644 --- a/js/xpconnect/src/XPCConvert.cpp +++ b/js/xpconnect/src/XPCConvert.cpp @@ -23,6 +23,7 @@ #include "jsapi.h" #include "jsfriendapi.h" +#include "jsprf.h" #include "JavaScriptParent.h" #include "mozilla/dom/BindingUtils.h" diff --git a/js/xpconnect/src/XPCDebug.cpp b/js/xpconnect/src/XPCDebug.cpp index ce48d43f3ec1..84e142d06512 100644 --- a/js/xpconnect/src/XPCDebug.cpp +++ b/js/xpconnect/src/XPCDebug.cpp @@ -5,6 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "xpcprivate.h" +#include "jsdbgapi.h" +#include "jsprf.h" #ifdef XP_WIN #include diff --git a/js/xpconnect/src/XPCException.cpp b/js/xpconnect/src/XPCException.cpp index 580d6dc9d768..fe887b2689de 100644 --- a/js/xpconnect/src/XPCException.cpp +++ b/js/xpconnect/src/XPCException.cpp @@ -7,6 +7,7 @@ /* An implementaion of nsIException. */ #include "xpcprivate.h" +#include "jsprf.h" #include "nsError.h" #include "nsIUnicodeDecoder.h" diff --git a/js/xpconnect/src/XPCInlines.h b/js/xpconnect/src/XPCInlines.h index cd847dbbec56..0a05024d587a 100644 --- a/js/xpconnect/src/XPCInlines.h +++ b/js/xpconnect/src/XPCInlines.h @@ -12,8 +12,6 @@ #include -#include "jsfriendapi.h" - /***************************************************************************/ inline void diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 6ba8dd6c37bb..748d5a4da54a 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -31,7 +31,9 @@ #include "nsCCUncollectableMarker.h" #include "nsCycleCollectionNoteRootCallback.h" #include "nsScriptLoader.h" +#include "jsdbgapi.h" #include "jsfriendapi.h" +#include "jsprf.h" #include "js/MemoryMetrics.h" #include "mozilla/dom/DOMJSClass.h" #include "mozilla/dom/BindingUtils.h" diff --git a/js/xpconnect/src/XPCMaps.h b/js/xpconnect/src/XPCMaps.h index 4a477296fa86..396d3e7f0807 100644 --- a/js/xpconnect/src/XPCMaps.h +++ b/js/xpconnect/src/XPCMaps.h @@ -11,8 +11,6 @@ #include "mozilla/MemoryReporting.h" -#include "js/HashTable.h" -#include "jsfriendapi.h" // Maps... diff --git a/js/xpconnect/src/XPCQuickStubs.cpp b/js/xpconnect/src/XPCQuickStubs.cpp index 0f4174a6a670..7d3ddb528da7 100644 --- a/js/xpconnect/src/XPCQuickStubs.cpp +++ b/js/xpconnect/src/XPCQuickStubs.cpp @@ -7,6 +7,7 @@ #include "jsapi.h" #include "jsfriendapi.h" +#include "jsprf.h" #include "nsCOMPtr.h" #include "xpcprivate.h" #include "XPCInlines.h" diff --git a/js/xpconnect/src/XPCStack.cpp b/js/xpconnect/src/XPCStack.cpp index 7bc1a83fe61d..5b647c3cb204 100644 --- a/js/xpconnect/src/XPCStack.cpp +++ b/js/xpconnect/src/XPCStack.cpp @@ -7,6 +7,8 @@ /* Implements nsIStackFrame. */ #include "xpcprivate.h" +#include "jsdbgapi.h" +#include "jsprf.h" class XPCJSStackFrame : public nsIStackFrame { diff --git a/js/xpconnect/src/XPCThrower.cpp b/js/xpconnect/src/XPCThrower.cpp index b9169dd25d77..538cd5ce4bed 100644 --- a/js/xpconnect/src/XPCThrower.cpp +++ b/js/xpconnect/src/XPCThrower.cpp @@ -9,6 +9,7 @@ #include "xpcprivate.h" #include "xpcpublic.h" #include "XPCWrapper.h" +#include "jsprf.h" bool XPCThrower::sVerbose = true; diff --git a/js/xpconnect/src/XPCVariant.cpp b/js/xpconnect/src/XPCVariant.cpp index e965bab2fa84..cd07b2ee1f64 100644 --- a/js/xpconnect/src/XPCVariant.cpp +++ b/js/xpconnect/src/XPCVariant.cpp @@ -11,6 +11,7 @@ #include "nsCxPusher.h" #include "jsfriendapi.h" +#include "jsprf.h" using namespace JS; using namespace mozilla; diff --git a/js/xpconnect/src/XPCWrappedJS.cpp b/js/xpconnect/src/XPCWrappedJS.cpp index be131eb9ff0d..0e0e5278e323 100644 --- a/js/xpconnect/src/XPCWrappedJS.cpp +++ b/js/xpconnect/src/XPCWrappedJS.cpp @@ -7,6 +7,7 @@ /* Class that wraps JS objects to appear as XPCOM objects. */ #include "xpcprivate.h" +#include "jsprf.h" #include "nsCxPusher.h" #include "nsContentUtils.h" #include "nsProxyRelease.h" diff --git a/js/xpconnect/src/XPCWrappedJSClass.cpp b/js/xpconnect/src/XPCWrappedJSClass.cpp index 2bdc729e2361..970d4bb0dabf 100644 --- a/js/xpconnect/src/XPCWrappedJSClass.cpp +++ b/js/xpconnect/src/XPCWrappedJSClass.cpp @@ -8,6 +8,7 @@ /* Sharable code and data for wrapper around JSObjects. */ #include "xpcprivate.h" +#include "jsprf.h" #include "nsArrayEnumerator.h" #include "nsContentUtils.h" #include "nsWrapperCache.h" diff --git a/js/xpconnect/src/XPCWrappedNative.cpp b/js/xpconnect/src/XPCWrappedNative.cpp index bace7f43ff25..379b1220425b 100644 --- a/js/xpconnect/src/XPCWrappedNative.cpp +++ b/js/xpconnect/src/XPCWrappedNative.cpp @@ -14,6 +14,7 @@ #include "XPCLog.h" #include "nsINode.h" #include "XPCQuickStubs.h" +#include "jsprf.h" #include "jsproxy.h" #include "AccessCheck.h" #include "WrapperFactory.h" diff --git a/js/xpconnect/src/XPCWrappedNativeInfo.cpp b/js/xpconnect/src/XPCWrappedNativeInfo.cpp index a6367fa1a803..c97170ea90d7 100644 --- a/js/xpconnect/src/XPCWrappedNativeInfo.cpp +++ b/js/xpconnect/src/XPCWrappedNativeInfo.cpp @@ -7,6 +7,7 @@ /* Manage the shared info about interfaces for use by wrappedNatives. */ #include "xpcprivate.h" +#include "jswrapper.h" #include "nsCxPusher.h" #include "mozilla/MemoryReporting.h" diff --git a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp index 7df18c2a73fc..4c042a78af88 100644 --- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp +++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp @@ -10,6 +10,7 @@ #include "xpcprivate.h" #include "XPCWrapper.h" #include "AccessCheck.h" +#include "jsprf.h" #include "nsWrapperCacheInlines.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/Preferences.h" diff --git a/js/xpconnect/src/XPCWrapper.h b/js/xpconnect/src/XPCWrapper.h index 49a8ff4eafd4..e5d58626946d 100644 --- a/js/xpconnect/src/XPCWrapper.h +++ b/js/xpconnect/src/XPCWrapper.h @@ -9,6 +9,7 @@ #include "xpcprivate.h" #include "xpcpublic.h" +#include "jswrapper.h" class nsIScriptSecurityManager; diff --git a/js/xpconnect/src/nsScriptError.cpp b/js/xpconnect/src/nsScriptError.cpp index 732febbabde8..0f4d437b901c 100644 --- a/js/xpconnect/src/nsScriptError.cpp +++ b/js/xpconnect/src/nsScriptError.cpp @@ -9,6 +9,7 @@ */ #include "xpcprivate.h" +#include "jsprf.h" #include "nsGlobalWindow.h" #include "nsPIDOMWindow.h" #include "nsILoadContext.h" diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index b8e8bdeb1681..f415ec2daa67 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -16,6 +16,7 @@ #include "XPCWrapper.h" #include "nsBaseHashtable.h" #include "nsHashKeys.h" +#include "jsdbgapi.h" #include "jsfriendapi.h" #include "dom_quickstubs.h" #include "nsNullPrincipal.h" diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index c96995f89fd2..9042a47fb210 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -92,14 +92,7 @@ #include #include "xpcpublic.h" -#include "jsapi.h" -#include "jsprf.h" #include "pldhash.h" -#include "prprf.h" -#include "jsdbgapi.h" -#include "jsfriendapi.h" -#include "js/HeapAPI.h" -#include "jswrapper.h" #include "nscore.h" #include "nsXPCOM.h" #include "nsAutoPtr.h" @@ -135,8 +128,6 @@ #include "nsXPIDLString.h" #include "nsAutoJSValHolder.h" -#include "js/HashTable.h" - #include "nsThreadUtils.h" #include "nsIJSEngineTelemetryStats.h" diff --git a/js/xpconnect/src/xpcpublic.h b/js/xpconnect/src/xpcpublic.h index d36063f6fbda..a829a57c5bf1 100644 --- a/js/xpconnect/src/xpcpublic.h +++ b/js/xpconnect/src/xpcpublic.h @@ -8,10 +8,6 @@ #ifndef xpcpublic_h #define xpcpublic_h -#include "jsapi.h" -#include "jsclass.h" -#include "jsfriendapi.h" -#include "jspubtd.h" #include "jsproxy.h" #include "js/HeapAPI.h" #include "js/GCAPI.h" diff --git a/js/xpconnect/tests/components/native/xpctest_params.cpp b/js/xpconnect/tests/components/native/xpctest_params.cpp index 4ea7aeabfdf5..38b50bdf0cff 100644 --- a/js/xpconnect/tests/components/native/xpctest_params.cpp +++ b/js/xpconnect/tests/components/native/xpctest_params.cpp @@ -4,6 +4,7 @@ #include "xpctest_private.h" #include "xpctest_interfaces.h" +#include "js/Value.h" NS_IMPL_ISUPPORTS1(nsXPCTestParams, nsIXPCTestParams) diff --git a/js/xpconnect/tests/components/native/xpctest_private.h b/js/xpconnect/tests/components/native/xpctest_private.h index 8bf01370d4da..7a5fb894479f 100644 --- a/js/xpconnect/tests/components/native/xpctest_private.h +++ b/js/xpconnect/tests/components/native/xpctest_private.h @@ -11,7 +11,6 @@ #include "nsISupports.h" #include "nsMemory.h" -#include "jsapi.h" #include "nsStringGlue.h" #include "xpctest_attributes.h" #include "xpctest_params.h" diff --git a/js/xpconnect/wrappers/AccessCheck.h b/js/xpconnect/wrappers/AccessCheck.h index 3571d5522eb1..20eb44e87a87 100644 --- a/js/xpconnect/wrappers/AccessCheck.h +++ b/js/xpconnect/wrappers/AccessCheck.h @@ -8,7 +8,6 @@ #ifndef __AccessCheck_h__ #define __AccessCheck_h__ -#include "jsapi.h" #include "jswrapper.h" class nsIPrincipal; diff --git a/js/xpconnect/wrappers/WaiveXrayWrapper.h b/js/xpconnect/wrappers/WaiveXrayWrapper.h index 16d020d7ec8e..ea0337e9ce76 100644 --- a/js/xpconnect/wrappers/WaiveXrayWrapper.h +++ b/js/xpconnect/wrappers/WaiveXrayWrapper.h @@ -10,7 +10,6 @@ #include "mozilla/Attributes.h" -#include "jsapi.h" #include "jswrapper.h" namespace xpc { diff --git a/js/xpconnect/wrappers/WrapperFactory.h b/js/xpconnect/wrappers/WrapperFactory.h index 199cc4afde6e..a86b8ecf1641 100644 --- a/js/xpconnect/wrappers/WrapperFactory.h +++ b/js/xpconnect/wrappers/WrapperFactory.h @@ -8,7 +8,6 @@ #ifndef _xpc_WRAPPERFACTORY_H #define _xpc_WRAPPERFACTORY_H -#include "jsapi.h" #include "jswrapper.h" namespace xpc { diff --git a/js/xpconnect/wrappers/XrayWrapper.cpp b/js/xpconnect/wrappers/XrayWrapper.cpp index bc561544c7f0..a62019dd30f3 100644 --- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -18,6 +18,7 @@ #include "xpcprivate.h" #include "jsapi.h" +#include "jsprf.h" #include "nsJSUtils.h" #include "mozilla/dom/BindingUtils.h" diff --git a/js/xpconnect/wrappers/XrayWrapper.h b/js/xpconnect/wrappers/XrayWrapper.h index 873e09a1213e..868c298d740a 100644 --- a/js/xpconnect/wrappers/XrayWrapper.h +++ b/js/xpconnect/wrappers/XrayWrapper.h @@ -8,7 +8,6 @@ #include "mozilla/Attributes.h" #include "mozilla/GuardObjects.h" -#include "jsapi.h" #include "jswrapper.h" // Xray wrappers re-resolve the original native properties on the native @@ -80,10 +79,10 @@ class XrayWrapper : public Base { virtual bool defineProperty(JSContext *cx, JS::Handle wrapper, JS::Handle id, JS::MutableHandle desc); virtual bool getOwnPropertyNames(JSContext *cx, JS::Handle wrapper, - js::AutoIdVector &props); + JS::AutoIdVector &props); virtual bool delete_(JSContext *cx, JS::Handle wrapper, JS::Handle id, bool *bp); - virtual bool enumerate(JSContext *cx, JS::Handle wrapper, js::AutoIdVector &props); + virtual bool enumerate(JSContext *cx, JS::Handle wrapper, JS::AutoIdVector &props); /* Derived proxy traps. */ virtual bool get(JSContext *cx, JS::Handle wrapper, JS::Handle receiver, @@ -95,7 +94,7 @@ class XrayWrapper : public Base { virtual bool hasOwn(JSContext *cx, JS::Handle wrapper, JS::Handle id, bool *bp); virtual bool keys(JSContext *cx, JS::Handle wrapper, - js::AutoIdVector &props); + JS::AutoIdVector &props); virtual bool iterate(JSContext *cx, JS::Handle wrapper, unsigned flags, JS::MutableHandle vp); diff --git a/layout/style/nsNthIndexCache.h b/layout/style/nsNthIndexCache.h index 03eff9ff3c56..3c11d58b492b 100644 --- a/layout/style/nsNthIndexCache.h +++ b/layout/style/nsNthIndexCache.h @@ -5,7 +5,6 @@ #ifndef nsContentIndexCache_h__ #define nsContentIndexCache_h__ -#include "js/HashTable.h" #include "mozilla/dom/Element.h" /* diff --git a/media/webrtc/signaling/src/peerconnection/MediaStreamList.h b/media/webrtc/signaling/src/peerconnection/MediaStreamList.h index 2b45984aa0bd..bea887405d25 100644 --- a/media/webrtc/signaling/src/peerconnection/MediaStreamList.h +++ b/media/webrtc/signaling/src/peerconnection/MediaStreamList.h @@ -8,7 +8,6 @@ #include "mozilla/ErrorResult.h" #include "nsISupportsImpl.h" #include "nsAutoPtr.h" -#include "jspubtd.h" #include "nsWrapperCache.h" #ifdef USE_FAKE_MEDIA_STREAMS diff --git a/netwerk/base/src/ArrayBufferInputStream.h b/netwerk/base/src/ArrayBufferInputStream.h index 9dd079f9e16e..b8b7755448e9 100644 --- a/netwerk/base/src/ArrayBufferInputStream.h +++ b/netwerk/base/src/ArrayBufferInputStream.h @@ -8,7 +8,7 @@ #include "nsIArrayBufferInputStream.h" #include "mozilla/Util.h" -#include "jsapi.h" +#include "js/Value.h" #define NS_ARRAYBUFFERINPUTSTREAM_CONTRACTID "@mozilla.org/io/arraybuffer-input-stream;1" #define NS_ARRAYBUFFERINPUTSTREAM_CID \ diff --git a/netwerk/base/src/ProxyAutoConfig.h b/netwerk/base/src/ProxyAutoConfig.h index c43d67cc6f95..92e0c66099d3 100644 --- a/netwerk/base/src/ProxyAutoConfig.h +++ b/netwerk/base/src/ProxyAutoConfig.h @@ -8,12 +8,15 @@ #define ProxyAutoConfig_h__ #include "nsString.h" -#include "jsapi.h" #include "prio.h" #include "nsITimer.h" #include "nsAutoPtr.h" #include "mozilla/net/DNS.h" +namespace JS { +class Value; +} + namespace mozilla { namespace net { class JSRuntimeWrapper; diff --git a/security/manager/ssl/src/nsCrypto.h b/security/manager/ssl/src/nsCrypto.h index cd2720be1674..9187a70c162a 100644 --- a/security/manager/ssl/src/nsCrypto.h +++ b/security/manager/ssl/src/nsCrypto.h @@ -15,7 +15,6 @@ #include "nsIDOMCryptoLegacy.h" #include "nsIRunnable.h" #include "nsString.h" -#include "jsapi.h" #include "nsIPrincipal.h" #define NS_CRYPTO_CID \ diff --git a/storage/src/mozStorageAsyncStatementJSHelper.h b/storage/src/mozStorageAsyncStatementJSHelper.h index 93c4132d37b1..77485799f38a 100644 --- a/storage/src/mozStorageAsyncStatementJSHelper.h +++ b/storage/src/mozStorageAsyncStatementJSHelper.h @@ -26,7 +26,7 @@ public: NS_DECL_NSIXPCSCRIPTABLE private: - nsresult getParams(AsyncStatement *, JSContext *, JSObject *, jsval *); + nsresult getParams(AsyncStatement *, JSContext *, JSObject *, JS::Value *); }; } // namespace storage diff --git a/storage/src/mozStorageAsyncStatementParams.cpp b/storage/src/mozStorageAsyncStatementParams.cpp index 31d89ac6d46d..c69ab239ae20 100644 --- a/storage/src/mozStorageAsyncStatementParams.cpp +++ b/storage/src/mozStorageAsyncStatementParams.cpp @@ -8,6 +8,8 @@ #include "nsString.h" #include "nsCOMPtr.h" +#include "jsapi.h" + #include "mozStoragePrivateHelpers.h" #include "mozStorageAsyncStatement.h" #include "mozStorageAsyncStatementParams.h" @@ -47,7 +49,7 @@ AsyncStatementParams::SetProperty( JSContext *aCtx, JSObject *aScopeObj, jsid aId, - jsval *_vp, + JS::Value *_vp, bool *_retval ) { diff --git a/storage/src/mozStoragePrivateHelpers.h b/storage/src/mozStoragePrivateHelpers.h index 790fa5ea689d..a82a5c4d8f49 100644 --- a/storage/src/mozStoragePrivateHelpers.h +++ b/storage/src/mozStoragePrivateHelpers.h @@ -14,7 +14,6 @@ #include "sqlite3.h" #include "nsIVariant.h" #include "nsError.h" -#include "jsapi.h" #include "nsAutoPtr.h" class mozIStorageCompletionCallback; @@ -22,6 +21,10 @@ class mozIStorageBaseStatement; class mozIStorageBindingParams; class nsIRunnable; +namespace JS { +class Value; +} + namespace mozilla { namespace storage { @@ -55,7 +58,7 @@ nsresult convertResultCode(int aSQLiteResultCode); void checkAndLogStatementPerformance(sqlite3_stmt *aStatement); /** - * Convert the provided jsval into a variant representation if possible. + * Convert the provided JS::Value into a variant representation if possible. * * @param aCtx * The JSContext the value is from. @@ -66,7 +69,7 @@ void checkAndLogStatementPerformance(sqlite3_stmt *aStatement); * @return the variant if conversion was successful, nullptr if conversion * failed. The caller is responsible for addref'ing if non-null. */ -nsIVariant *convertJSValToVariant(JSContext *aCtx, jsval aValue); +nsIVariant *convertJSValToVariant(JSContext *aCtx, JS::Value aValue); /** * Obtains an event that will notify a completion callback about completion. diff --git a/storage/src/mozStorageStatementJSHelper.h b/storage/src/mozStorageStatementJSHelper.h index 1b1517ee2838..b83570733463 100644 --- a/storage/src/mozStorageStatementJSHelper.h +++ b/storage/src/mozStorageStatementJSHelper.h @@ -21,8 +21,8 @@ public: NS_DECL_NSIXPCSCRIPTABLE private: - nsresult getRow(Statement *, JSContext *, JSObject *, jsval *); - nsresult getParams(Statement *, JSContext *, JSObject *, jsval *); + nsresult getRow(Statement *, JSContext *, JSObject *, JS::Value *); + nsresult getParams(Statement *, JSContext *, JSObject *, JS::Value *); }; } // namespace storage diff --git a/storage/src/mozStorageStatementParams.cpp b/storage/src/mozStorageStatementParams.cpp index cc9562f1eebd..17b478dbdd67 100644 --- a/storage/src/mozStorageStatementParams.cpp +++ b/storage/src/mozStorageStatementParams.cpp @@ -7,6 +7,8 @@ #include "nsMemory.h" #include "nsString.h" +#include "jsapi.h" + #include "mozStoragePrivateHelpers.h" #include "mozStorageStatementParams.h" #include "mozIStorageStatement.h" @@ -46,7 +48,7 @@ StatementParams::SetProperty(nsIXPConnectWrappedNative *aWrapper, JSContext *aCtx, JSObject *aScopeObj, jsid aId, - jsval *_vp, + JS::Value *_vp, bool *_retval) { NS_ENSURE_TRUE(mStatement, NS_ERROR_NOT_INITIALIZED); diff --git a/toolkit/components/ctypes/tests/jsctypes-test-errno.h b/toolkit/components/ctypes/tests/jsctypes-test-errno.h index 96d2762805fd..23762f7a628d 100644 --- a/toolkit/components/ctypes/tests/jsctypes-test-errno.h +++ b/toolkit/components/ctypes/tests/jsctypes-test-errno.h @@ -5,7 +5,6 @@ #include "nscore.h" #include "prtypes.h" -#include "jsapi.h" #define EXPORT_CDECL(type) NS_EXPORT type #define EXPORT_STDCALL(type) NS_EXPORT type NS_STDCALL diff --git a/toolkit/components/ctypes/tests/jsctypes-test-finalizer.h b/toolkit/components/ctypes/tests/jsctypes-test-finalizer.h index cca2f166d48a..807275ba23d5 100644 --- a/toolkit/components/ctypes/tests/jsctypes-test-finalizer.h +++ b/toolkit/components/ctypes/tests/jsctypes-test-finalizer.h @@ -2,8 +2,6 @@ * 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 "jsapi.h" - #define EXPORT_CDECL(type) NS_EXPORT type NS_EXTERN_C diff --git a/toolkit/components/ctypes/tests/jsctypes-test.h b/toolkit/components/ctypes/tests/jsctypes-test.h index 143e81e1448e..6f1f5e8fd86b 100644 --- a/toolkit/components/ctypes/tests/jsctypes-test.h +++ b/toolkit/components/ctypes/tests/jsctypes-test.h @@ -5,7 +5,7 @@ #include "nscore.h" #include "prtypes.h" -#include "jsapi.h" +#include "jspubtd.h" #define EXPORT_CDECL(type) NS_EXPORT type #define EXPORT_STDCALL(type) NS_EXPORT type NS_STDCALL diff --git a/toolkit/components/places/Helpers.h b/toolkit/components/places/Helpers.h index 893e5f9b10c9..607fde81bc46 100644 --- a/toolkit/components/places/Helpers.h +++ b/toolkit/components/places/Helpers.h @@ -15,7 +15,6 @@ #include "nsThreadUtils.h" #include "nsProxyRelease.h" #include "mozilla/Telemetry.h" -#include "jsapi.h" namespace mozilla { namespace places { diff --git a/toolkit/components/places/PlaceInfo.cpp b/toolkit/components/places/PlaceInfo.cpp index e2a4695917aa..8e2a3c06fce5 100644 --- a/toolkit/components/places/PlaceInfo.cpp +++ b/toolkit/components/places/PlaceInfo.cpp @@ -8,6 +8,7 @@ #include "nsServiceManagerUtils.h" #include "nsIXPConnect.h" #include "mozilla/Services.h" +#include "jsapi.h" namespace mozilla { namespace places { diff --git a/toolkit/components/places/mozIAsyncHistory.idl b/toolkit/components/places/mozIAsyncHistory.idl index 925a62aaec79..35c8cc3a662a 100644 --- a/toolkit/components/places/mozIAsyncHistory.idl +++ b/toolkit/components/places/mozIAsyncHistory.idl @@ -7,10 +7,6 @@ interface nsIURI; interface nsIVariant; -%{C++ -#include "jsapi.h" -%} - [scriptable, uuid(41e4ccc9-f0c8-4cd7-9753-7a38514b8488)] interface mozIVisitInfo : nsISupports { diff --git a/toolkit/components/places/mozIAsyncLivemarks.idl b/toolkit/components/places/mozIAsyncLivemarks.idl index b49a63973f2e..68aae9d104e9 100644 --- a/toolkit/components/places/mozIAsyncLivemarks.idl +++ b/toolkit/components/places/mozIAsyncLivemarks.idl @@ -4,10 +4,6 @@ #include "nsISupports.idl" -%{C++ -#include "jsapi.h" -%} - interface nsIURI; interface mozILivemarkCallback; diff --git a/tools/profiler/GeckoProfiler.h b/tools/profiler/GeckoProfiler.h index 57f6b7e2d3dc..62688072d9ee 100644 --- a/tools/profiler/GeckoProfiler.h +++ b/tools/profiler/GeckoProfiler.h @@ -49,7 +49,6 @@ #ifndef SAMPLER_H #define SAMPLER_H -#include "jsfriendapi.h" #include "mozilla/NullPtr.h" #include "mozilla/TimeStamp.h" diff --git a/tools/profiler/GeckoProfilerFunc.h b/tools/profiler/GeckoProfilerFunc.h index 9c4c837044b0..3e6130867af2 100644 --- a/tools/profiler/GeckoProfilerFunc.h +++ b/tools/profiler/GeckoProfilerFunc.h @@ -9,11 +9,13 @@ #include "mozilla/NullPtr.h" #include #include "mozilla/TimeStamp.h" -#include "jsfriendapi.h" using mozilla::TimeStamp; using mozilla::TimeDuration; +struct JSContext; +class JSObject; + // Returns a handle to pass on exit. This can check that we are popping the // correct callstack. inline void* mozilla_sampler_call_enter(const char *aInfo, void *aFrameAddress = NULL, diff --git a/tools/profiler/GeckoProfilerImpl.h b/tools/profiler/GeckoProfilerImpl.h index 02e53ba8b493..b2ea4305fc3e 100644 --- a/tools/profiler/GeckoProfilerImpl.h +++ b/tools/profiler/GeckoProfilerImpl.h @@ -16,7 +16,6 @@ #include "mozilla/Util.h" #include "nsAlgorithm.h" #include "nscore.h" -#include "jsfriendapi.h" #include "GeckoProfilerFunc.h" #include "PseudoStack.h" #include "nsISupports.h" diff --git a/tools/profiler/JSObjectBuilder.h b/tools/profiler/JSObjectBuilder.h index fa3deb25552b..3f597d742c7d 100644 --- a/tools/profiler/JSObjectBuilder.h +++ b/tools/profiler/JSObjectBuilder.h @@ -11,6 +11,7 @@ class JSCustomObject; class JSCustomObjectBuilder; struct JSContext; +class JSObject; class nsAString; /* this is handy wrapper around JSAPI to make it more pleasant to use. diff --git a/tools/profiler/SaveProfileTask.h b/tools/profiler/SaveProfileTask.h index 8ea01cc03aa5..ed48ffe2601c 100644 --- a/tools/profiler/SaveProfileTask.h +++ b/tools/profiler/SaveProfileTask.h @@ -12,7 +12,6 @@ #include "nsDirectoryServiceUtils.h" #include "nsDirectoryServiceDefs.h" #include "nsXULAppAPI.h" -#include "jsfriendapi.h" #include "nsIJSRuntimeService.h" #include "nsIProfileSaveEvent.h" diff --git a/xpcom/base/CycleCollectedJSRuntime.h b/xpcom/base/CycleCollectedJSRuntime.h index 6fbed1c79802..0712c61fa335 100644 --- a/xpcom/base/CycleCollectedJSRuntime.h +++ b/xpcom/base/CycleCollectedJSRuntime.h @@ -9,7 +9,7 @@ #include "mozilla/MemoryReporting.h" #include "jsapi.h" -#include "jsfriendapi.h" +#include "jsclass.h" #include "nsCycleCollector.h" #include "nsCycleCollectionParticipant.h" diff --git a/xpcom/build/mozPoisonWriteMac.cpp b/xpcom/build/mozPoisonWriteMac.cpp index 5a865f0694c8..bd28532e3bb5 100644 --- a/xpcom/build/mozPoisonWriteMac.cpp +++ b/xpcom/build/mozPoisonWriteMac.cpp @@ -9,6 +9,7 @@ #include "mozilla/Util.h" #include "nsTraceRefcntImpl.h" #include "mozilla/Assertions.h" +#include "mozilla/DebugOnly.h" #include "mozilla/Scoped.h" #include "mozilla/Mutex.h" #include "mozilla/Telemetry.h" diff --git a/xpcom/reflect/xptcall/public/xptcall.h b/xpcom/reflect/xptcall/public/xptcall.h index 01e50469cb09..af32024c7e1c 100644 --- a/xpcom/reflect/xptcall/public/xptcall.h +++ b/xpcom/reflect/xptcall/public/xptcall.h @@ -12,7 +12,7 @@ #include "nsISupports.h" #include "xpt_struct.h" #include "xptinfo.h" -#include "jsapi.h" +#include "js/Value.h" struct nsXPTCMiniVariant { @@ -38,7 +38,7 @@ struct nsXPTCMiniVariant // Types below here are unknown to the assembly implementations, and // therefore _must_ be passed with indirect semantics. We put them in // the union here for type safety, so that we can avoid void* tricks. - jsval j; + JS::Value j; } val; }; @@ -75,7 +75,7 @@ struct nsXPTCVariant : public nsXPTCMiniVariant PTR_IS_DATA = 0x1, // Indicates that the value we hold requires some sort of cleanup (memory - // deallocation, interface release, jsval unrooting, etc). The precise + // deallocation, interface release, JS::Value unrooting, etc). The precise // cleanup that is performed depends on the 'type' field above. // If the value is an array, this flag specifies whether the elements // within the array require cleanup (we always clean up the array itself, diff --git a/xpcom/tests/TestHarness.h b/xpcom/tests/TestHarness.h index 2f28fd35a201..c13a63a61b18 100644 --- a/xpcom/tests/TestHarness.h +++ b/xpcom/tests/TestHarness.h @@ -36,7 +36,6 @@ #include "nsIProperties.h" #include "nsIObserverService.h" #include "nsXULAppAPI.h" -#include "jsdbgapi.h" #include #include #include From d07c2c7839ff70cd8e540c09fc8fd11acfa19b92 Mon Sep 17 00:00:00 2001 From: Gabor Krizsanits Date: Thu, 22 Aug 2013 08:26:37 +0200 Subject: [PATCH 06/54] Bug 886237 - Splitting up XPCComponents. r=bholley --- js/xpconnect/src/Sandbox.cpp | 1572 ++++++++++++++++++++++++++++ js/xpconnect/src/XPCComponents.cpp | 1545 +-------------------------- js/xpconnect/src/moz.build | 1 + js/xpconnect/src/xpcprivate.h | 15 + 4 files changed, 1594 insertions(+), 1539 deletions(-) create mode 100644 js/xpconnect/src/Sandbox.cpp diff --git a/js/xpconnect/src/Sandbox.cpp b/js/xpconnect/src/Sandbox.cpp new file mode 100644 index 000000000000..c97b052ff2a3 --- /dev/null +++ b/js/xpconnect/src/Sandbox.cpp @@ -0,0 +1,1572 @@ +/* -*- 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/. */ + +/* + * The Components.Sandbox object. + */ + +#include "AccessCheck.h" +#include "jsfriendapi.h" +#include "jsproxy.h" +#include "nsContentUtils.h" +#include "nsCxPusher.h" +#include "nsGlobalWindow.h" +#include "nsIDOMWindow.h" +#include "nsIScriptContext.h" +#include "nsIScriptObjectPrincipal.h" +#include "nsIScriptSecurityManager.h" +#include "nsIURI.h" +#include "nsJSEnvironment.h" +#include "nsJSUtils.h" +#include "nsNetUtil.h" +#include "nsNullPrincipal.h" +#include "nsPrincipal.h" +#include "nsXMLHttpRequest.h" +#include "WrapperFactory.h" +#include "XPCJSWeakReference.h" +#include "xpcprivate.h" +#include "XPCQuickStubs.h" +#include "XPCWrapper.h" +#include "XrayWrapper.h" +#include "mozilla/dom/BindingUtils.h" + +using namespace mozilla; +using namespace js; +using namespace xpc; + +using mozilla::dom::DestroyProtoAndIfaceCache; + +NS_IMPL_ISUPPORTS3(SandboxPrivate, + nsIScriptObjectPrincipal, + nsIGlobalObject, + nsISupportsWeakReference) + +const char kScriptSecurityManagerContractID[] = NS_SCRIPTSECURITYMANAGER_CONTRACTID; + +class nsXPCComponents_utils_Sandbox : public nsIXPCComponents_utils_Sandbox, + public nsIXPCScriptable +{ +public: + // Aren't macros nice? + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIXPCCOMPONENTS_UTILS_SANDBOX + NS_DECL_NSIXPCSCRIPTABLE + +public: + nsXPCComponents_utils_Sandbox(); + virtual ~nsXPCComponents_utils_Sandbox(); + +private: + static nsresult CallOrConstruct(nsIXPConnectWrappedNative *wrapper, + JSContext *cx, HandleObject obj, + const CallArgs &args, bool *_retval); +}; + +already_AddRefed +NewSandboxConstructor() +{ + nsCOMPtr sbConstructor = + new nsXPCComponents_utils_Sandbox(); + return sbConstructor.forget(); +} + +static bool +SandboxDump(JSContext *cx, unsigned argc, jsval *vp) +{ + JSString *str; + if (!argc) + return true; + + str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]); + if (!str) + return false; + + size_t length; + const jschar *chars = JS_GetStringCharsZAndLength(cx, str, &length); + if (!chars) + return false; + + nsDependentString wstr(chars, length); + char *cstr = ToNewUTF8String(wstr); + if (!cstr) + return false; + +#if defined(XP_MACOSX) + // Be nice and convert all \r to \n. + char *c = cstr, *cEnd = cstr + strlen(cstr); + while (c < cEnd) { + if (*c == '\r') + *c = '\n'; + c++; + } +#endif + + fputs(cstr, stdout); + fflush(stdout); + NS_Free(cstr); + JS_SET_RVAL(cx, vp, JSVAL_TRUE); + return true; +} + +static bool +SandboxDebug(JSContext *cx, unsigned argc, jsval *vp) +{ +#ifdef DEBUG + return SandboxDump(cx, argc, vp); +#else + return true; +#endif +} + +static bool +SandboxImport(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + if (args.length() < 1 || args[0].isPrimitive()) { + XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx); + return false; + } + + RootedString funname(cx); + if (args.length() > 1) { + // Use the second parameter as the function name. + funname = JS_ValueToString(cx, args[1]); + if (!funname) + return false; + } else { + // NB: funobj must only be used to get the JSFunction out. + RootedObject funobj(cx, &args[0].toObject()); + if (js::IsProxy(funobj)) { + funobj = XPCWrapper::UnsafeUnwrapSecurityWrapper(funobj); + } + + JSAutoCompartment ac(cx, funobj); + + JSFunction *fun = JS_ValueToFunction(cx, ObjectValue(*funobj)); + if (!fun) { + XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx); + return false; + } + + // Use the actual function name as the name. + funname = JS_GetFunctionId(fun); + if (!funname) { + XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx); + return false; + } + } + + RootedId id(cx); + if (!JS_ValueToId(cx, StringValue(funname), id.address())) + return false; + + // We need to resolve the this object, because this function is used + // unbound and should still work and act on the original sandbox. + RootedObject thisObject(cx, JS_THIS_OBJECT(cx, vp)); + if (!thisObject) { + XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx); + return false; + } + if (!JS_SetPropertyById(cx, thisObject, id, args[0])) + return false; + + args.rval().setUndefined(); + return true; +} + +static bool +CreateXMLHttpRequest(JSContext *cx, unsigned argc, jsval *vp) +{ + nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); + if (!ssm) + return false; + + nsIPrincipal *subjectPrincipal = ssm->GetCxSubjectPrincipal(cx); + if (!subjectPrincipal) + return false; + + RootedObject global(cx, JS::CurrentGlobalOrNull(cx)); + MOZ_ASSERT(global); + + nsIScriptObjectPrincipal *sop = + static_cast(xpc_GetJSPrivate(global)); + nsCOMPtr iglobal = do_QueryInterface(sop); + + nsCOMPtr xhr = new nsXMLHttpRequest(); + nsresult rv = xhr->Init(subjectPrincipal, nullptr, iglobal, nullptr); + if (NS_FAILED(rv)) + return false; + + rv = nsContentUtils::WrapNative(cx, global, xhr, vp); + if (NS_FAILED(rv)) + return false; + + return true; +} + +/* + * Instead of simply wrapping a function into another compartment, + * this helper function creates a native function in the target + * compartment and forwards the call to the original function. + * That call will be different than a regular JS function call in + * that, the |this| is left unbound, and all the non-native JS + * object arguments will be cloned using the structured clone + * algorithm. + * The return value is the new forwarder function, wrapped into + * the caller's compartment. + * The 3rd argument is the name of the property that will + * be set on the target scope, with the forwarder function as + * the value. + * The principal of the caller must subsume that of the target. + * + * Expected type of the arguments and the return value: + * function exportFunction(function funToExport, + * object targetScope, + * string name) + */ +static bool +ExportFunction(JSContext *cx, unsigned argc, jsval *vp) +{ + MOZ_ASSERT(cx); + if (argc < 3) { + JS_ReportError(cx, "Function requires at least 3 arguments"); + return false; + } + + CallArgs args = CallArgsFromVp(argc, vp); + if (!args[0].isObject() || !args[1].isObject() || !args[2].isString()) { + JS_ReportError(cx, "Invalid argument"); + return false; + } + + RootedObject funObj(cx, &args[0].toObject()); + RootedObject targetScope(cx, &args[1].toObject()); + RootedString funName(cx, args[2].toString()); + + // We can only export functions to scopes those are transparent for us, + // so if there is a security wrapper around targetScope we must throw. + targetScope = CheckedUnwrap(targetScope); + if (!targetScope) { + JS_ReportError(cx, "Permission denied to export function into scope"); + return false; + } + + if (JS_GetStringLength(funName) == 0) { + JS_ReportError(cx, "3rd argument should be a non-empty string"); + return false; + } + + { + // We need to operate in the target scope from here on, let's enter + // its compartment. + JSAutoCompartment ac(cx, targetScope); + + // Unwrapping to see if we have a callable. + funObj = UncheckedUnwrap(funObj); + if (!JS_ObjectIsCallable(cx, funObj)) { + JS_ReportError(cx, "First argument must be a function"); + return false; + } + + // The function forwarder will live in the target compartment. Since + // this function will be referenced from its private slot, to avoid a + // GC hazard, we must wrap it to the same compartment. + if (!JS_WrapObject(cx, funObj.address())) + return false; + + RootedId id(cx); + if (!JS_ValueToId(cx, args[2], id.address())) + return false; + + // And now, let's create the forwarder function in the target compartment + // for the function the be exported. + if (!NewFunctionForwarder(cx, id, funObj, /* doclone = */ true, args.rval())) { + JS_ReportError(cx, "Exporting function failed"); + return false; + } + + // We have the forwarder function in the target compartment, now + // we have to add it to the target scope as a property. + if (!JS_DefinePropertyById(cx, targetScope, id, args.rval(), + JS_PropertyStub, JS_StrictPropertyStub, + JSPROP_ENUMERATE)) + return false; + } + + // Finally we have to re-wrap the exported function back to the caller compartment. + if (!JS_WrapValue(cx, args.rval().address())) + return false; + + return true; +} + +static bool +GetFilenameAndLineNumber(JSContext *cx, nsACString &filename, unsigned &lineno) +{ + JSScript *script; + if (JS_DescribeScriptedCaller(cx, &script, &lineno)) { + if (const char *cfilename = JS_GetScriptFilename(cx, script)) { + filename.Assign(nsDependentCString(cfilename)); + return true; + } + } + return false; +} + +namespace xpc { +bool +IsReflector(JSObject *obj) +{ + return IS_WN_REFLECTOR(obj) || dom::IsDOMObject(obj); +} +} /* namespace xpc */ + +enum ForwarderCloneTags { + SCTAG_BASE = JS_SCTAG_USER_MIN, + SCTAG_REFLECTOR +}; + +static JSObject * +CloneNonReflectorsRead(JSContext *cx, JSStructuredCloneReader *reader, uint32_t tag, + uint32_t data, void *closure) +{ + MOZ_ASSERT(closure, "Null pointer!"); + AutoObjectVector *reflectors = static_cast(closure); + if (tag == SCTAG_REFLECTOR) { + MOZ_ASSERT(!data); + + size_t idx; + if (JS_ReadBytes(reader, &idx, sizeof(size_t))) { + RootedObject reflector(cx, reflectors->handleAt(idx)); + MOZ_ASSERT(reflector, "No object pointer?"); + MOZ_ASSERT(IsReflector(reflector), "Object pointer must be a reflector!"); + + JS_WrapObject(cx, reflector.address()); + JS_ASSERT(WrapperFactory::IsXrayWrapper(reflector) || + IsReflector(reflector)); + + return reflector; + } + } + + JS_ReportError(cx, "CloneNonReflectorsRead error"); + return nullptr; +} + +static bool +CloneNonReflectorsWrite(JSContext *cx, JSStructuredCloneWriter *writer, + Handle obj, void *closure) +{ + MOZ_ASSERT(closure, "Null pointer!"); + + // We need to maintain a list of reflectors to make sure all these objects + // are properly rooter. Only their indices will be serialized. + AutoObjectVector *reflectors = static_cast(closure); + if (IsReflector(obj)) { + if (!reflectors->append(obj)) + return false; + + size_t idx = reflectors->length()-1; + if (JS_WriteUint32Pair(writer, SCTAG_REFLECTOR, 0) && + JS_WriteBytes(writer, &idx, sizeof(size_t))) { + return true; + } + } + + JS_ReportError(cx, "CloneNonReflectorsWrite error"); + return false; +} + +JSStructuredCloneCallbacks gForwarderStructuredCloneCallbacks = { + CloneNonReflectorsRead, + CloneNonReflectorsWrite, + nullptr +}; + +/* + * This is a special structured cloning, that clones only non-reflectors. + * The function assumes the cx is already entered the compartment we want + * to clone to, and that if val is an object is from the compartment we + * clone from. + */ +bool +CloneNonReflectors(JSContext *cx, MutableHandleValue val) +{ + JSAutoStructuredCloneBuffer buffer; + AutoObjectVector rootedReflectors(cx); + { + // For parsing val we have to enter its compartment. + // (unless it's a primitive) + Maybe ac; + if (val.isObject()) { + ac.construct(cx, &val.toObject()); + } + + if (!buffer.write(cx, val, + &gForwarderStructuredCloneCallbacks, + &rootedReflectors)) + { + return false; + } + } + + // Now recreate the clones in the target compartment. + RootedValue rval(cx); + if (!buffer.read(cx, val.address(), + &gForwarderStructuredCloneCallbacks, + &rootedReflectors)) + { + return false; + } + + return true; +} + +/* + * Similar to evalInSandbox except this one is used to eval a script in the + * scope of a window. Also note, that the return value and the possible exceptions + * in the script are structured cloned, unless they are natives (then they are just + * wrapped). + * Principal of the caller must subsume the target's. + * + * Expected type of the arguments: + * value evalInWindow(string script, + * object window) + */ +static bool +EvalInWindow(JSContext *cx, unsigned argc, jsval *vp) +{ + MOZ_ASSERT(cx); + if (argc < 2) { + JS_ReportError(cx, "Function requires two arguments"); + return false; + } + + CallArgs args = CallArgsFromVp(argc, vp); + if (!args[0].isString() || !args[1].isObject()) { + JS_ReportError(cx, "Invalid arguments"); + return false; + } + + RootedString srcString(cx, args[0].toString()); + RootedObject targetScope(cx, &args[1].toObject()); + + // If we cannot unwrap we must not eval in it. + targetScope = CheckedUnwrap(targetScope); + if (!targetScope) { + JS_ReportError(cx, "Permission denied to eval in target scope"); + return false; + } + + // Make sure that we have a window object. + RootedObject inner(cx, CheckedUnwrap(targetScope, /* stopAtOuter = */ false)); + nsCOMPtr global; + nsCOMPtr window; + if (!JS_IsGlobalObject(inner) || + !(global = GetNativeForGlobal(inner)) || + !(window = do_QueryInterface(global))) + { + JS_ReportError(cx, "Second argument must be a window"); + return false; + } + + nsCOMPtr context = + (static_cast(window.get()))->GetScriptContext(); + if (!context) { + JS_ReportError(cx, "Script context needed"); + return false; + } + + if (!context->GetScriptsEnabled()) { + JS_ReportError(cx, "Scripts are disabled in this window"); + return false; + } + + nsCString filename; + unsigned lineNo; + if (!GetFilenameAndLineNumber(cx, filename, lineNo)) { + // Default values for non-scripted callers. + filename.Assign("Unknown"); + lineNo = 0; + } + + nsDependentJSString srcDepString; + srcDepString.init(cx, srcString); + + { + // CompileOptions must be created from the context + // we will execute this script in. + JSContext *wndCx = context->GetNativeContext(); + AutoCxPusher pusher(wndCx); + JS::CompileOptions compileOptions(wndCx); + compileOptions.setFileAndLine(filename.get(), lineNo); + + // We don't want the JS engine to automatically report + // uncaught exceptions. + nsJSUtils::EvaluateOptions evaluateOptions; + evaluateOptions.setReportUncaught(false); + + nsresult rv = nsJSUtils::EvaluateString(wndCx, + srcDepString, + targetScope, + compileOptions, + evaluateOptions, + args.rval().address()); + + if (NS_FAILED(rv)) { + // If there was an exception we get it as a return value, if + // the evaluation failed for some other reason, then a default + // exception is raised. + MOZ_ASSERT(!JS_IsExceptionPending(wndCx), + "Exception should be delivered as return value."); + if (args.rval().isUndefined()) { + MOZ_ASSERT(rv == NS_ERROR_OUT_OF_MEMORY); + return false; + } + + // If there was an exception thrown we should set it + // on the calling context. + RootedValue exn(wndCx, args.rval()); + // First we should reset the return value. + args.rval().set(UndefinedValue()); + + // Then clone the exception. + if (CloneNonReflectors(cx, &exn)) + JS_SetPendingException(cx, exn); + + return false; + } + } + + // Let's clone the return value back to the callers compartment. + if (!CloneNonReflectors(cx, args.rval())) { + args.rval().set(UndefinedValue()); + return false; + } + + return true; +} + +static bool +sandbox_enumerate(JSContext *cx, HandleObject obj) +{ + return JS_EnumerateStandardClasses(cx, obj); +} + +static bool +sandbox_resolve(JSContext *cx, HandleObject obj, HandleId id) +{ + bool resolved; + return JS_ResolveStandardClass(cx, obj, id, &resolved); +} + +static void +sandbox_finalize(JSFreeOp *fop, JSObject *obj) +{ + nsIScriptObjectPrincipal *sop = + static_cast(xpc_GetJSPrivate(obj)); + MOZ_ASSERT(sop); + static_cast(sop)->ForgetGlobalObject(); + NS_IF_RELEASE(sop); + DestroyProtoAndIfaceCache(obj); +} + +static bool +sandbox_convert(JSContext *cx, HandleObject obj, JSType type, MutableHandleValue vp) +{ + if (type == JSTYPE_OBJECT) { + vp.set(OBJECT_TO_JSVAL(obj)); + return true; + } + + return JS_ConvertStub(cx, obj, type, vp); +} + +static JSClass SandboxClass = { + "Sandbox", + XPCONNECT_GLOBAL_FLAGS, + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, + sandbox_enumerate, sandbox_resolve, sandbox_convert, sandbox_finalize, + NULL, NULL, NULL, NULL, TraceXPCGlobal +}; + +static const JSFunctionSpec SandboxFunctions[] = { + JS_FS("dump", SandboxDump, 1,0), + JS_FS("debug", SandboxDebug, 1,0), + JS_FS("importFunction", SandboxImport, 1,0), + JS_FS_END +}; + +bool +IsSandbox(JSObject *obj) +{ + return GetObjectJSClass(obj) == &SandboxClass; +} + +/***************************************************************************/ +nsXPCComponents_utils_Sandbox::nsXPCComponents_utils_Sandbox() +{ +} + +nsXPCComponents_utils_Sandbox::~nsXPCComponents_utils_Sandbox() +{ +} + +NS_INTERFACE_MAP_BEGIN(nsXPCComponents_utils_Sandbox) + NS_INTERFACE_MAP_ENTRY(nsIXPCComponents_utils_Sandbox) + NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_utils_Sandbox) +NS_INTERFACE_MAP_END_THREADSAFE + +NS_IMPL_ADDREF(nsXPCComponents_utils_Sandbox) +NS_IMPL_RELEASE(nsXPCComponents_utils_Sandbox) + +// We use the nsIXPScriptable macros to generate lots of stuff for us. +#define XPC_MAP_CLASSNAME nsXPCComponents_utils_Sandbox +#define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_utils_Sandbox" +#define XPC_MAP_WANT_CALL +#define XPC_MAP_WANT_CONSTRUCT +#define XPC_MAP_FLAGS 0 +#include "xpc_map_end.h" /* This #undef's the above. */ + +xpc::SandboxProxyHandler xpc::sandboxProxyHandler; + +bool +xpc::IsSandboxPrototypeProxy(JSObject *obj) +{ + return js::IsProxy(obj) && + js::GetProxyHandler(obj) == &xpc::sandboxProxyHandler; +} + +bool +xpc::SandboxCallableProxyHandler::call(JSContext *cx, JS::Handle proxy, + const JS::CallArgs &args) +{ + // We forward the call to our underlying callable. + + // The parent of our proxy is the SandboxProxyHandler proxy + RootedObject sandboxProxy(cx, JS_GetParent(proxy)); + MOZ_ASSERT(js::IsProxy(sandboxProxy) && + js::GetProxyHandler(sandboxProxy) == &xpc::sandboxProxyHandler); + + // The parent of the sandboxProxy is the sandbox global, and the + // target object is the original proto. + RootedObject sandboxGlobal(cx, JS_GetParent(sandboxProxy)); + MOZ_ASSERT(js::GetObjectJSClass(sandboxGlobal) == &SandboxClass); + + // If our this object is the sandbox global, we call with this set to the + // original proto instead. + // + // There are two different ways we can compute |this|. If we use + // JS_THIS_VALUE, we'll get the bonafide |this| value as passed by the + // caller, which may be undefined if a global function was invoked without + // an explicit invocant. If we use JS_THIS or JS_THIS_OBJECT, the |this| + // in |vp| will be coerced to the global, which is not the correct + // behavior in ES5 strict mode. And we have no way to compute strictness + // here. + // + // The naive approach is simply to use JS_THIS_VALUE here. If |this| was + // explicit, we can remap it appropriately. If it was implicit, then we + // leave it as undefined, and let the callee sort it out. Since the callee + // is generally in the same compartment as its global (eg the Window's + // compartment, not the Sandbox's), the callee will generally compute the + // correct |this|. + // + // However, this breaks down in the Xray case. If the sandboxPrototype + // is an Xray wrapper, then we'll end up reifying the native methods in + // the Sandbox's scope, which means that they'll compute |this| to be the + // Sandbox, breaking old-style XPC_WN_CallMethod methods. + // + // Luckily, the intent of Xrays is to provide a vanilla view of a foreign + // DOM interface, which means that we don't care about script-enacted + // strictness in the prototype's home compartment. Indeed, since DOM + // methods are always non-strict, we can just assume non-strict semantics + // if the sandboxPrototype is an Xray Wrapper, which lets us appropriately + // remap |this|. + JS::Value thisVal = + WrapperFactory::IsXrayWrapper(sandboxProxy) ? args.computeThis(cx) : args.thisv(); + if (thisVal == ObjectValue(*sandboxGlobal)) { + thisVal = ObjectValue(*js::GetProxyTargetObject(sandboxProxy)); + } + + return JS::Call(cx, thisVal, js::GetProxyPrivate(proxy), args.length(), args.array(), + args.rval()); +} + +xpc::SandboxCallableProxyHandler xpc::sandboxCallableProxyHandler; + +// Wrap a callable such that if we're called with oldThisObj as the +// "this" we will instead call it with newThisObj as the this. +static JSObject* +WrapCallable(JSContext *cx, JSObject *callable, JSObject *sandboxProtoProxy) +{ + MOZ_ASSERT(JS_ObjectIsCallable(cx, callable)); + // Our proxy is wrapping the callable. So we need to use the + // callable as the private. We use the given sandboxProtoProxy as + // the parent, and our call() hook depends on that. + MOZ_ASSERT(js::IsProxy(sandboxProtoProxy) && + js::GetProxyHandler(sandboxProtoProxy) == + &xpc::sandboxProxyHandler); + + RootedValue priv(cx, ObjectValue(*callable)); + return js::NewProxyObject(cx, &xpc::sandboxCallableProxyHandler, + priv, nullptr, + sandboxProtoProxy, js::ProxyIsCallable); +} + +template +bool BindPropertyOp(JSContext *cx, Op &op, JSPropertyDescriptor *desc, HandleId id, + unsigned attrFlag, HandleObject sandboxProtoProxy) +{ + if (!op) { + return true; + } + + RootedObject func(cx); + if (desc->attrs & attrFlag) { + // Already an object + func = JS_FUNC_TO_DATA_PTR(JSObject *, op); + } else { + // We have an actual property op. For getters, we use 0 + // args, for setters we use 1 arg. + uint32_t args = (attrFlag == JSPROP_GETTER) ? 0 : 1; + RootedObject obj(cx, desc->obj); + func = GeneratePropertyOp(cx, obj, id, args, op); + if (!func) + return false; + } + func = WrapCallable(cx, func, sandboxProtoProxy); + if (!func) + return false; + op = JS_DATA_TO_FUNC_PTR(Op, func.get()); + desc->attrs |= attrFlag; + return true; +} + +extern bool +XPC_WN_Helper_GetProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp); +extern bool +XPC_WN_Helper_SetProperty(JSContext *cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp); + +bool +xpc::SandboxProxyHandler::getPropertyDescriptor(JSContext *cx, + JS::Handle proxy, + JS::Handle id, + JS::MutableHandle desc, + unsigned flags) +{ + JS::RootedObject obj(cx, wrappedObject(proxy)); + + MOZ_ASSERT(js::GetObjectCompartment(obj) == js::GetObjectCompartment(proxy)); + if (!JS_GetPropertyDescriptorById(cx, obj, id, + flags, desc)) + return false; + + if (!desc.object()) + return true; // No property, nothing to do + + // Now fix up the getter/setter/value as needed to be bound to desc->obj + // Don't mess with holder_get and holder_set, though, because those rely on + // the "vp is prefilled with the value in the slot" behavior that property + // ops can in theory rely on, but our property op forwarder doesn't know how + // to make that happen. Since we really only need to rebind the DOM methods + // here, not rebindings holder_get and holder_set is OK. + // + // Similarly, don't mess with XPC_WN_Helper_GetProperty and + // XPC_WN_Helper_SetProperty, for the same reasons: that could confuse our + // access to expandos when we're not doing Xrays. + if (desc.getter() != xpc::holder_get && + desc.getter() != XPC_WN_Helper_GetProperty && + !BindPropertyOp(cx, desc.getter(), desc.address(), id, JSPROP_GETTER, proxy)) + return false; + if (desc.setter() != xpc::holder_set && + desc.setter() != XPC_WN_Helper_SetProperty && + !BindPropertyOp(cx, desc.setter(), desc.address(), id, JSPROP_SETTER, proxy)) + return false; + if (desc.value().isObject()) { + JSObject* val = &desc.value().toObject(); + if (JS_ObjectIsCallable(cx, val)) { + val = WrapCallable(cx, val, proxy); + if (!val) + return false; + desc.value().setObject(*val); + } + } + + return true; +} + +bool +xpc::SandboxProxyHandler::getOwnPropertyDescriptor(JSContext *cx, + JS::Handle proxy, + JS::Handle id, + JS::MutableHandle desc, + unsigned flags) +{ + if (!getPropertyDescriptor(cx, proxy, id, desc, flags)) + return false; + + if (desc.object() != wrappedObject(proxy)) + desc.object().set(nullptr); + + return true; +} + +/* + * Reuse the BaseProxyHandler versions of the derived traps that are implemented + * in terms of the fundamental traps. + */ + +bool +xpc::SandboxProxyHandler::has(JSContext *cx, JS::Handle proxy, + JS::Handle id, bool *bp) +{ + return BaseProxyHandler::has(cx, proxy, id, bp); +} +bool +xpc::SandboxProxyHandler::hasOwn(JSContext *cx, JS::Handle proxy, + JS::Handle id, bool *bp) +{ + return BaseProxyHandler::hasOwn(cx, proxy, id, bp); +} + +bool +xpc::SandboxProxyHandler::get(JSContext *cx, JS::Handle proxy, + JS::Handle receiver, + JS::Handle id, + JS::MutableHandle vp) +{ + return BaseProxyHandler::get(cx, proxy, receiver, id, vp); +} + +bool +xpc::SandboxProxyHandler::set(JSContext *cx, JS::Handle proxy, + JS::Handle receiver, + JS::Handle id, + bool strict, + JS::MutableHandle vp) +{ + return BaseProxyHandler::set(cx, proxy, receiver, id, strict, vp); +} + +bool +xpc::SandboxProxyHandler::keys(JSContext *cx, JS::Handle proxy, + AutoIdVector &props) +{ + return BaseProxyHandler::keys(cx, proxy, props); +} + +bool +xpc::SandboxProxyHandler::iterate(JSContext *cx, JS::Handle proxy, + unsigned flags, JS::MutableHandle vp) +{ + return BaseProxyHandler::iterate(cx, proxy, flags, vp); +} + +nsresult +xpc_CreateSandboxObject(JSContext *cx, jsval *vp, nsISupports *prinOrSop, SandboxOptions& options) +{ + // Create the sandbox global object + nsresult rv; + nsCOMPtr xpc(do_GetService(nsIXPConnect::GetCID(), &rv)); + if (NS_FAILED(rv)) + return NS_ERROR_XPC_UNEXPECTED; + + nsCOMPtr principal = do_QueryInterface(prinOrSop); + if (!principal) { + nsCOMPtr sop = do_QueryInterface(prinOrSop); + if (sop) { + principal = sop->GetPrincipal(); + } else { + principal = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv); + MOZ_ASSERT(NS_FAILED(rv) || principal, "Bad return from do_CreateInstance"); + + if (!principal || NS_FAILED(rv)) { + if (NS_SUCCEEDED(rv)) { + rv = NS_ERROR_FAILURE; + } + + return rv; + } + } + MOZ_ASSERT(principal); + } + + JS::CompartmentOptions compartmentOptions; + compartmentOptions.setZone(options.sameZoneAs + ? JS::SameZoneAs(js::UncheckedUnwrap(options.sameZoneAs)) + : JS::SystemZone); + RootedObject sandbox(cx, xpc::CreateGlobalObject(cx, &SandboxClass, + principal, compartmentOptions)); + if (!sandbox) + return NS_ERROR_FAILURE; + + // Set up the wantXrays flag, which indicates whether xrays are desired even + // for same-origin access. + // + // This flag has historically been ignored for chrome sandboxes due to + // quirks in the wrapping implementation that have now been removed. Indeed, + // same-origin Xrays for chrome->chrome access seems a bit superfluous. + // Arguably we should just flip the default for chrome and still honor the + // flag, but such a change would break code in subtle ways for minimal + // benefit. So we just switch it off here. + xpc::GetCompartmentPrivate(sandbox)->wantXrays = + AccessCheck::isChrome(sandbox) ? false : options.wantXrays; + + { + JSAutoCompartment ac(cx, sandbox); + + if (options.proto) { + bool ok = JS_WrapObject(cx, options.proto.address()); + if (!ok) + return NS_ERROR_XPC_UNEXPECTED; + + if (xpc::WrapperFactory::IsXrayWrapper(options.proto) && !options.wantXrays) { + RootedValue v(cx, ObjectValue(*options.proto)); + if (!xpc::WrapperFactory::WaiveXrayAndWrap(cx, v.address())) + return NS_ERROR_FAILURE; + options.proto = &v.toObject(); + } + + // Now check what sort of thing we've got in |proto| + JSObject *unwrappedProto = js::UncheckedUnwrap(options.proto, false); + js::Class *unwrappedClass = js::GetObjectClass(unwrappedProto); + if (IS_WN_CLASS(unwrappedClass) || + mozilla::dom::IsDOMClass(Jsvalify(unwrappedClass))) { + // Wrap it up in a proxy that will do the right thing in terms + // of this-binding for methods. + RootedValue priv(cx, ObjectValue(*options.proto)); + options.proto = js::NewProxyObject(cx, &xpc::sandboxProxyHandler, + priv, nullptr, sandbox); + if (!options.proto) + return NS_ERROR_OUT_OF_MEMORY; + } + + ok = JS_SetPrototype(cx, sandbox, options.proto); + if (!ok) + return NS_ERROR_XPC_UNEXPECTED; + } + + nsCOMPtr sbp = + new SandboxPrivate(principal, sandbox); + + // Pass on ownership of sbp to |sandbox|. + JS_SetPrivate(sandbox, sbp.forget().get()); + + bool allowComponents = nsContentUtils::IsSystemPrincipal(principal) || + nsContentUtils::IsExpandedPrincipal(principal); + if (options.wantComponents && allowComponents && + !nsXPCComponents::AttachComponentsObject(cx, GetObjectScope(sandbox))) + return NS_ERROR_XPC_UNEXPECTED; + + if (!XPCNativeWrapper::AttachNewConstructorObject(cx, sandbox)) + return NS_ERROR_XPC_UNEXPECTED; + + if (!JS_DefineFunctions(cx, sandbox, SandboxFunctions)) + return NS_ERROR_XPC_UNEXPECTED; + + if (options.wantXHRConstructor && + !JS_DefineFunction(cx, sandbox, "XMLHttpRequest", CreateXMLHttpRequest, 0, JSFUN_CONSTRUCTOR)) + return NS_ERROR_XPC_UNEXPECTED; + + if (options.wantExportHelpers && + (!JS_DefineFunction(cx, sandbox, "exportFunction", ExportFunction, 3, 0) || + !JS_DefineFunction(cx, sandbox, "evalInWindow", EvalInWindow, 2, 0))) + return NS_ERROR_XPC_UNEXPECTED; + + } + + if (vp) { + // We have this crazy behavior where wantXrays=false also implies that the + // returned sandbox is implicitly waived. We've stopped advertising it, but + // keep supporting it for now. + *vp = OBJECT_TO_JSVAL(sandbox); + if (options.wantXrays && !JS_WrapValue(cx, vp)) + return NS_ERROR_UNEXPECTED; + if (!options.wantXrays && !xpc::WrapperFactory::WaiveXrayAndWrap(cx, vp)) + return NS_ERROR_UNEXPECTED; + } + + // Set the location information for the new global, so that tools like + // about:memory may use that information + xpc::SetLocationForGlobal(sandbox, options.sandboxName); + + JS_FireOnNewGlobalObject(cx, sandbox); + + return NS_OK; +} + +/* bool call(in nsIXPConnectWrappedNative wrapper, + in JSContextPtr cx, + in JSObjectPtr obj, + in uint32_t argc, + in JSValPtr argv, + in JSValPtr vp); +*/ +NS_IMETHODIMP +nsXPCComponents_utils_Sandbox::Call(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *objArg, const CallArgs &args, bool *_retval) +{ + RootedObject obj(cx, objArg); + return CallOrConstruct(wrapper, cx, obj, args, _retval); +} + +/* bool construct(in nsIXPConnectWrappedNative wrapper, + in JSContextPtr cx, + in JSObjectPtr obj, + in uint32_t argc, + in JSValPtr argv, + in JSValPtr vp); +*/ +NS_IMETHODIMP +nsXPCComponents_utils_Sandbox::Construct(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *objArg, const CallArgs &args, bool *_retval) +{ + RootedObject obj(cx, objArg); + return CallOrConstruct(wrapper, cx, obj, args, _retval); +} + +// for sandbox constructor the first argument can be a URI string in which case +// we use the related Codebase Principal for the sandbox +nsresult +GetPrincipalFromString(JSContext *cx, HandleString codebase, nsIPrincipal **principal) +{ + MOZ_ASSERT(principal); + MOZ_ASSERT(codebase); + nsCOMPtr uri; + nsDependentJSString codebaseStr; + NS_ENSURE_TRUE(codebaseStr.init(cx, codebase), NS_ERROR_FAILURE); + nsresult rv = NS_NewURI(getter_AddRefs(uri), codebaseStr); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr secman = + do_GetService(kScriptSecurityManagerContractID); + NS_ENSURE_TRUE(secman, NS_ERROR_FAILURE); + + // We could allow passing in the app-id and browser-element info to the + // sandbox constructor. But creating a sandbox based on a string is a + // deprecated API so no need to add features to it. + rv = secman->GetNoAppCodebasePrincipal(uri, principal); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(*principal, NS_ERROR_FAILURE); + + return NS_OK; +} + +// for sandbox constructor the first argument can be a principal object or +// a script object principal (Document, Window) +nsresult +GetPrincipalOrSOP(JSContext *cx, HandleObject from, nsISupports **out) +{ + MOZ_ASSERT(out); + *out = NULL; + + nsXPConnect* xpc = nsXPConnect::XPConnect(); + nsCOMPtr wrapper; + xpc->GetWrappedNativeOfJSObject(cx, from, + getter_AddRefs(wrapper)); + + NS_ENSURE_TRUE(wrapper, NS_ERROR_INVALID_ARG); + + if (nsCOMPtr sop = do_QueryWrappedNative(wrapper)) { + sop.forget(out); + return NS_OK; + } + + nsCOMPtr principal = do_QueryWrappedNative(wrapper); + principal.forget(out); + NS_ENSURE_TRUE(*out, NS_ERROR_INVALID_ARG); + + return NS_OK; +} + +// the first parameter of the sandbox constructor might be an array of principals, either in string +// format or actual objects (see GetPrincipalOrSOP) +nsresult +GetExpandedPrincipal(JSContext *cx, HandleObject arrayObj, nsIExpandedPrincipal **out) +{ + MOZ_ASSERT(out); + uint32_t length; + + if (!JS_IsArrayObject(cx, arrayObj) || + !JS_GetArrayLength(cx, arrayObj, &length) || + !length) + { + // we need a white list of principals or uri strings to create an + // expanded principal, if we got an empty array or something else + // report error + return NS_ERROR_INVALID_ARG; + } + + nsTArray< nsCOMPtr > allowedDomains(length); + allowedDomains.SetLength(length); + nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); + NS_ENSURE_TRUE(ssm, NS_ERROR_XPC_UNEXPECTED); + + for (uint32_t i = 0; i < length; ++i) { + RootedValue allowed(cx); + if (!JS_GetElement(cx, arrayObj, i, &allowed)) + return NS_ERROR_INVALID_ARG; + + nsresult rv; + nsCOMPtr principal; + if (allowed.isString()) { + // in case of string let's try to fetch a codebase principal from it + RootedString str(cx, allowed.toString()); + rv = GetPrincipalFromString(cx, str, getter_AddRefs(principal)); + NS_ENSURE_SUCCESS(rv, rv); + } else if (allowed.isObject()) { + // in case of object let's see if it's a Principal or a ScriptObjectPrincipal + nsCOMPtr prinOrSop; + RootedObject obj(cx, &allowed.toObject()); + rv = GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr sop(do_QueryInterface(prinOrSop)); + principal = do_QueryInterface(prinOrSop); + if (sop) { + principal = sop->GetPrincipal(); + } + } + NS_ENSURE_TRUE(principal, NS_ERROR_INVALID_ARG); + + // We do not allow ExpandedPrincipals to contain any system principals + bool isSystem; + rv = ssm->IsSystemPrincipal(principal, &isSystem); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_FALSE(isSystem, NS_ERROR_INVALID_ARG); + allowedDomains[i] = principal; + } + + nsCOMPtr result = new nsExpandedPrincipal(allowedDomains); + result.forget(out); + return NS_OK; +} + +// helper that tries to get a property form the options object +nsresult +GetPropFromOptions(JSContext *cx, HandleObject from, const char *name, MutableHandleValue prop, + bool *found) +{ + if (!JS_HasProperty(cx, from, name, found)) + return NS_ERROR_INVALID_ARG; + + if (found && !JS_GetProperty(cx, from, name, prop)) + return NS_ERROR_INVALID_ARG; + + return NS_OK; +} + +// helper that tries to get a boolean property form the options object +nsresult +GetBoolPropFromOptions(JSContext *cx, HandleObject from, const char *name, bool *prop) +{ + MOZ_ASSERT(prop); + + + RootedValue value(cx); + bool found; + if (NS_FAILED(GetPropFromOptions(cx, from, name, &value, &found))) + return NS_ERROR_INVALID_ARG; + + if (!found) + return NS_OK; + + if (!value.isBoolean()) + return NS_ERROR_INVALID_ARG; + + *prop = value.toBoolean(); + return NS_OK; +} + +// helper that tries to get an object property form the options object +nsresult +GetObjPropFromOptions(JSContext *cx, HandleObject from, const char *name, JSObject **prop) +{ + MOZ_ASSERT(prop); + + RootedValue value(cx); + bool found; + if (NS_FAILED(GetPropFromOptions(cx, from, name, &value, &found))) + return NS_ERROR_INVALID_ARG; + + if (!found) { + *prop = NULL; + return NS_OK; + } + + if (!value.isObject()) + return NS_ERROR_INVALID_ARG; + + *prop = &value.toObject(); + return NS_OK; +} + +// helper that tries to get a string property form the options object +nsresult +GetStringPropFromOptions(JSContext *cx, HandleObject from, const char *name, nsCString &prop) +{ + RootedValue value(cx); + bool found; + nsresult rv = GetPropFromOptions(cx, from, name, &value, &found); + NS_ENSURE_SUCCESS(rv, rv); + + if (!found) + return NS_OK; + + NS_ENSURE_TRUE(value.isString(), NS_ERROR_INVALID_ARG); + + char *tmp = JS_EncodeString(cx, value.toString()); + NS_ENSURE_TRUE(tmp, NS_ERROR_INVALID_ARG); + prop.Adopt(tmp, strlen(tmp)); + return NS_OK; +} + +// helper that parsing the sandbox options object (from) and sets the fields of the incoming options struct (options) +nsresult +ParseOptionsObject(JSContext *cx, jsval from, SandboxOptions &options) +{ + NS_ENSURE_TRUE(from.isObject(), NS_ERROR_INVALID_ARG); + RootedObject optionsObject(cx, &from.toObject()); + nsresult rv = GetObjPropFromOptions(cx, optionsObject, + "sandboxPrototype", options.proto.address()); + NS_ENSURE_SUCCESS(rv, rv); + + rv = GetBoolPropFromOptions(cx, optionsObject, + "wantXrays", &options.wantXrays); + NS_ENSURE_SUCCESS(rv, rv); + + rv = GetBoolPropFromOptions(cx, optionsObject, + "wantComponents", &options.wantComponents); + NS_ENSURE_SUCCESS(rv, rv); + + rv = GetBoolPropFromOptions(cx, optionsObject, + "wantXHRConstructor", &options.wantXHRConstructor); + NS_ENSURE_SUCCESS(rv, rv); + + rv = GetBoolPropFromOptions(cx, optionsObject, + "wantExportHelpers", &options.wantExportHelpers); + NS_ENSURE_SUCCESS(rv, rv); + + rv = GetStringPropFromOptions(cx, optionsObject, + "sandboxName", options.sandboxName); + NS_ENSURE_SUCCESS(rv, rv); + + rv = GetObjPropFromOptions(cx, optionsObject, + "sameZoneAs", options.sameZoneAs.address()); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +static nsresult +AssembleSandboxMemoryReporterName(JSContext *cx, nsCString &sandboxName) +{ + // Use a default name when the caller did not provide a sandboxName. + if (sandboxName.IsEmpty()) + sandboxName = NS_LITERAL_CSTRING("[anonymous sandbox]"); + + nsXPConnect* xpc = nsXPConnect::XPConnect(); + // Get the xpconnect native call context. + nsAXPCNativeCallContext *cc = nullptr; + xpc->GetCurrentNativeCallContext(&cc); + NS_ENSURE_TRUE(cc, NS_ERROR_INVALID_ARG); + + // Get the current source info from xpc. + nsCOMPtr frame; + xpc->GetCurrentJSStack(getter_AddRefs(frame)); + + // Append the caller's location information. + if (frame) { + nsCString location; + int32_t lineNumber = 0; + frame->GetFilename(getter_Copies(location)); + frame->GetLineNumber(&lineNumber); + + sandboxName.AppendLiteral(" (from: "); + sandboxName.Append(location); + sandboxName.AppendLiteral(":"); + sandboxName.AppendInt(lineNumber); + sandboxName.AppendLiteral(")"); + } + + return NS_OK; +} + +// static +nsresult +nsXPCComponents_utils_Sandbox::CallOrConstruct(nsIXPConnectWrappedNative *wrapper, + JSContext *cx, HandleObject obj, + const CallArgs &args, bool *_retval) +{ + if (args.length() < 1) + return ThrowAndFail(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx, _retval); + + nsresult rv; + + // Make sure to set up principals on the sandbox before initing classes + nsCOMPtr principal; + nsCOMPtr expanded; + nsCOMPtr prinOrSop; + + if (args[0].isString()) { + RootedString str(cx, args[0].toString()); + rv = GetPrincipalFromString(cx, str, getter_AddRefs(principal)); + prinOrSop = principal; + } else if (args[0].isObject()) { + RootedObject obj(cx, &args[0].toObject()); + if (JS_IsArrayObject(cx, obj)) { + rv = GetExpandedPrincipal(cx, obj, getter_AddRefs(expanded)); + prinOrSop = expanded; + } else { + rv = GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop)); + } + } else { + return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); + } + + if (NS_FAILED(rv)) + return ThrowAndFail(rv, cx, _retval); + + SandboxOptions options(cx); + + if (args.length() > 1 && args[1].isObject()) { + if (NS_FAILED(ParseOptionsObject(cx, args[1], options))) + return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); + } + + if (NS_FAILED(AssembleSandboxMemoryReporterName(cx, options.sandboxName))) + return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); + + rv = xpc_CreateSandboxObject(cx, args.rval().address(), prinOrSop, options); + + if (NS_FAILED(rv)) + return ThrowAndFail(rv, cx, _retval); + + *_retval = true; + + return rv; +} + +class ContextHolder : public nsIScriptObjectPrincipal +{ +public: + ContextHolder(JSContext *aOuterCx, HandleObject aSandbox, nsIPrincipal *aPrincipal); + virtual ~ContextHolder(); + + JSContext * GetJSContext() + { + return mJSContext; + } + + nsIPrincipal * GetPrincipal() { return mPrincipal; } + + NS_DECL_ISUPPORTS + +private: + JSContext* mJSContext; + nsCOMPtr mPrincipal; +}; + +NS_IMPL_ISUPPORTS1(ContextHolder, nsIScriptObjectPrincipal) + +ContextHolder::ContextHolder(JSContext *aOuterCx, + HandleObject aSandbox, + nsIPrincipal *aPrincipal) + : mJSContext(JS_NewContext(JS_GetRuntime(aOuterCx), 1024)), + mPrincipal(aPrincipal) +{ + if (mJSContext) { + bool isChrome; + DebugOnly rv = XPCWrapper::GetSecurityManager()-> + IsSystemPrincipal(mPrincipal, &isChrome); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + JS_SetOptions(mJSContext, + JS_GetOptions(mJSContext) | + JSOPTION_DONT_REPORT_UNCAUGHT | + JSOPTION_PRIVATE_IS_NSISUPPORTS); + js::SetDefaultObjectForContext(mJSContext, aSandbox); + JS_SetContextPrivate(mJSContext, this); + } +} + +ContextHolder::~ContextHolder() +{ + if (mJSContext) + JS_DestroyContextNoGC(mJSContext); +} + +nsresult +xpc_EvalInSandbox(JSContext *cx, HandleObject sandboxArg, const nsAString& source, + const char *filename, int32_t lineNo, + JSVersion jsVersion, bool returnStringOnly, MutableHandleValue rval) +{ + JS_AbortIfWrongThread(JS_GetRuntime(cx)); + rval.set(UndefinedValue()); + + bool waiveXray = xpc::WrapperFactory::HasWaiveXrayFlag(sandboxArg); + RootedObject sandbox(cx, js::CheckedUnwrap(sandboxArg)); + if (!sandbox || js::GetObjectJSClass(sandbox) != &SandboxClass) { + return NS_ERROR_INVALID_ARG; + } + + nsIScriptObjectPrincipal *sop = + (nsIScriptObjectPrincipal*)xpc_GetJSPrivate(sandbox); + MOZ_ASSERT(sop, "Invalid sandbox passed"); + nsCOMPtr prin = sop->GetPrincipal(); + NS_ENSURE_TRUE(prin, NS_ERROR_FAILURE); + + nsAutoCString filenameBuf; + if (!filename) { + // Default to the spec of the principal. + nsJSPrincipals::get(prin)->GetScriptLocation(filenameBuf); + filename = filenameBuf.get(); + lineNo = 1; + } + + // We create a separate cx to do the sandbox evaluation. Scope it. + RootedValue v(cx, UndefinedValue()); + RootedValue exn(cx, UndefinedValue()); + bool ok = true; + { + // Make a special cx for the sandbox and push it. + // NB: As soon as the RefPtr goes away, the cx goes away. So declare + // it first so that it disappears last. + nsRefPtr sandcxHolder = new ContextHolder(cx, sandbox, prin); + JSContext *sandcx = sandcxHolder->GetJSContext(); + if (!sandcx) { + JS_ReportError(cx, "Can't prepare context for evalInSandbox"); + return NS_ERROR_OUT_OF_MEMORY; + } + nsCxPusher pusher; + pusher.Push(sandcx); + JSAutoCompartment ac(sandcx, sandbox); + + JS::CompileOptions options(sandcx); + options.setPrincipals(nsJSPrincipals::get(prin)) + .setFileAndLine(filename, lineNo); + if (jsVersion != JSVERSION_DEFAULT) + options.setVersion(jsVersion); + JS::RootedObject rootedSandbox(sandcx, sandbox); + ok = JS::Evaluate(sandcx, rootedSandbox, options, + PromiseFlatString(source).get(), source.Length(), + v.address()); + if (ok && returnStringOnly && !v.isUndefined()) { + JSString *str = JS_ValueToString(sandcx, v); + ok = !!str; + v = ok ? JS::StringValue(str) : JS::UndefinedValue(); + } + + // If the sandbox threw an exception, grab it off the context. + if (JS_GetPendingException(sandcx, exn.address())) { + MOZ_ASSERT(!ok); + JS_ClearPendingException(sandcx); + if (returnStringOnly) { + // The caller asked for strings only, convert the + // exception into a string. + JSString *str = JS_ValueToString(sandcx, exn); + exn = str ? JS::StringValue(str) : JS::UndefinedValue(); + } + } + } + + // + // Alright, we're back on the caller's cx. If an error occured, try to + // wrap and set the exception. Otherwise, wrap the return value. + // + + if (!ok) { + // If we end up without an exception, it was probably due to OOM along + // the way, in which case we thow. Otherwise, wrap it. + if (exn.isUndefined() || !JS_WrapValue(cx, exn.address())) + return NS_ERROR_OUT_OF_MEMORY; + + // Set the exception on our caller's cx. + JS_SetPendingException(cx, exn); + return NS_ERROR_FAILURE; + } + + // Transitively apply Xray waivers if |sb| was waived. + if (waiveXray) { + ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, v.address()); + } else { + ok = JS_WrapValue(cx, v.address()); + } + NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE); + + // Whew! + rval.set(v); + return NS_OK; +} + +bool +NonCloningFunctionForwarder(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0)); + MOZ_ASSERT(v.isObject(), "weird function"); + + JSObject *obj = JS_THIS_OBJECT(cx, vp); + if (!obj) { + return false; + } + return JS_CallFunctionValue(cx, obj, v, args.length(), args.array(), vp); +} + +/* + * Forwards the call to the exported function. Clones all the non reflectors, ignores + * the |this| argument. + */ +bool +CloningFunctionForwarder(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0)); + NS_ASSERTION(v.isObject(), "weird function"); + RootedObject origFunObj(cx, UncheckedUnwrap(&v.toObject())); + { + JSAutoCompartment ac(cx, origFunObj); + // Note: only the arguments are cloned not the |this| or the |callee|. + // Function forwarder does not use those. + for (unsigned i = 0; i < args.length(); i++) { + if (!CloneNonReflectors(cx, args[i])) { + return false; + } + } + + // JS API does not support any JSObject to JSFunction conversion, + // so let's use JS_CallFunctionValue instead. + RootedValue functionVal(cx); + functionVal.setObject(*origFunObj); + + if (!JS_CallFunctionValue(cx, nullptr, functionVal, args.length(), args.array(), vp)) + return false; + } + + // Return value must be wrapped. + return JS_WrapValue(cx, vp); +} + +bool +NewFunctionForwarder(JSContext *cx, HandleId id, HandleObject callable, bool doclone, + MutableHandleValue vp) +{ + JSFunction *fun = js::NewFunctionByIdWithReserved(cx, doclone ? CloningFunctionForwarder : + NonCloningFunctionForwarder, + 0,0, JS::CurrentGlobalOrNull(cx), id); + + if (!fun) + return false; + + JSObject *funobj = JS_GetFunctionObject(fun); + js::SetFunctionNativeReserved(funobj, 0, ObjectValue(*callable)); + vp.setObject(*funobj); + return true; +} diff --git a/js/xpconnect/src/XPCComponents.cpp b/js/xpconnect/src/XPCComponents.cpp index 1ae811156239..c385e73a336b 100644 --- a/js/xpconnect/src/XPCComponents.cpp +++ b/js/xpconnect/src/XPCComponents.cpp @@ -40,17 +40,15 @@ #include "nsDOMClassInfoID.h" #include "nsGlobalWindow.h" - using namespace mozilla; using namespace js; using namespace xpc; -using mozilla::dom::DestroyProtoAndIfaceCache; - /***************************************************************************/ // stuff used by all -static nsresult ThrowAndFail(nsresult errNum, JSContext* cx, bool* retval) +nsresult +ThrowAndFail(nsresult errNum, JSContext* cx, bool* retval) { XPCThrower::Throw(errNum, cx); *retval = false; @@ -102,7 +100,6 @@ char * xpc_CheckAccessList(const PRUnichar* wideName, const char* const list[]) /***************************************************************************/ - class nsXPCComponents_Interfaces : public nsIXPCComponents_Interfaces, public nsIXPCScriptable, @@ -2588,27 +2585,6 @@ nsXPCComponents_Constructor::HasInstance(nsIXPConnectWrappedNative *wrapper, return NS_OK; } -/***************************************************************************/ -// Javascript constructor for the sandbox object -class nsXPCComponents_utils_Sandbox : public nsIXPCComponents_utils_Sandbox, - public nsIXPCScriptable -{ -public: - // Aren't macros nice? - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_NSIXPCCOMPONENTS_UTILS_SANDBOX - NS_DECL_NSIXPCSCRIPTABLE - -public: - nsXPCComponents_utils_Sandbox(); - virtual ~nsXPCComponents_utils_Sandbox(); - -private: - static nsresult CallOrConstruct(nsIXPConnectWrappedNative *wrapper, - JSContext *cx, HandleObject obj, - const CallArgs &args, bool *_retval); -}; - class nsXPCComponents_Utils : public nsIXPCComponents_Utils, public nsIXPCScriptable, @@ -2649,10 +2625,9 @@ NS_IMETHODIMP nsXPCComponents_Utils::GetSandbox(nsIXPCComponents_utils_Sandbox **aSandbox) { NS_ENSURE_ARG_POINTER(aSandbox); - if (!mSandbox && !(mSandbox = new nsXPCComponents_utils_Sandbox())) { - *aSandbox = nullptr; - return NS_ERROR_OUT_OF_MEMORY; - } + if (!mSandbox) + mSandbox = NewSandboxConstructor(); + NS_ADDREF(*aSandbox = mSandbox); return NS_OK; } @@ -2793,1344 +2768,6 @@ nsXPCComponents_Utils::ReportError(const JS::Value &errorArg, JSContext *cx) return NS_OK; } -#include "nsIScriptSecurityManager.h" -#include "nsIURI.h" -#include "nsNetUtil.h" -const char kScriptSecurityManagerContractID[] = NS_SCRIPTSECURITYMANAGER_CONTRACTID; - -NS_IMPL_ISUPPORTS3(SandboxPrivate, - nsIScriptObjectPrincipal, - nsIGlobalObject, - nsISupportsWeakReference) - -static bool -SandboxDump(JSContext *cx, unsigned argc, jsval *vp) -{ - JSString *str; - if (!argc) - return true; - - str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]); - if (!str) - return false; - - size_t length; - const jschar *chars = JS_GetStringCharsZAndLength(cx, str, &length); - if (!chars) - return false; - - nsDependentString wstr(chars, length); - char *cstr = ToNewUTF8String(wstr); - if (!cstr) - return false; - -#if defined(XP_MACOSX) - // Be nice and convert all \r to \n. - char *c = cstr, *cEnd = cstr + strlen(cstr); - while (c < cEnd) { - if (*c == '\r') - *c = '\n'; - c++; - } -#endif - - fputs(cstr, stdout); - fflush(stdout); - NS_Free(cstr); - JS_SET_RVAL(cx, vp, JSVAL_TRUE); - return true; -} - -static bool -SandboxDebug(JSContext *cx, unsigned argc, jsval *vp) -{ -#ifdef DEBUG - return SandboxDump(cx, argc, vp); -#else - return true; -#endif -} - -static bool -SandboxImport(JSContext *cx, unsigned argc, Value *vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - if (args.length() < 1 || args[0].isPrimitive()) { - XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx); - return false; - } - - RootedString funname(cx); - if (args.length() > 1) { - // Use the second parameter as the function name. - funname = JS_ValueToString(cx, args[1]); - if (!funname) - return false; - } else { - // NB: funobj must only be used to get the JSFunction out. - RootedObject funobj(cx, &args[0].toObject()); - if (js::IsProxy(funobj)) { - funobj = XPCWrapper::UnsafeUnwrapSecurityWrapper(funobj); - } - - JSAutoCompartment ac(cx, funobj); - - JSFunction *fun = JS_ValueToFunction(cx, ObjectValue(*funobj)); - if (!fun) { - XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx); - return false; - } - - // Use the actual function name as the name. - funname = JS_GetFunctionId(fun); - if (!funname) { - XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx); - return false; - } - } - - RootedId id(cx); - if (!JS_ValueToId(cx, StringValue(funname), id.address())) - return false; - - // We need to resolve the this object, because this function is used - // unbound and should still work and act on the original sandbox. - RootedObject thisObject(cx, JS_THIS_OBJECT(cx, vp)); - if (!thisObject) { - XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx); - return false; - } - if (!JS_SetPropertyById(cx, thisObject, id, args[0])) - return false; - - args.rval().setUndefined(); - return true; -} - -static bool -CreateXMLHttpRequest(JSContext *cx, unsigned argc, jsval *vp) -{ - nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); - if (!ssm) - return false; - - nsIPrincipal *subjectPrincipal = ssm->GetCxSubjectPrincipal(cx); - if (!subjectPrincipal) - return false; - - RootedObject global(cx, JS::CurrentGlobalOrNull(cx)); - MOZ_ASSERT(global); - - nsIScriptObjectPrincipal *sop = - static_cast(xpc_GetJSPrivate(global)); - nsCOMPtr iglobal = do_QueryInterface(sop); - - nsCOMPtr xhr = new nsXMLHttpRequest(); - nsresult rv = xhr->Init(subjectPrincipal, nullptr, iglobal, nullptr); - if (NS_FAILED(rv)) - return false; - - rv = nsContentUtils::WrapNative(cx, global, xhr, vp); - if (NS_FAILED(rv)) - return false; - - return true; -} - -bool -NewFunctionForwarder(JSContext *cx, HandleId id, HandleObject callable, - bool doclone, MutableHandleValue vp); - -/* - * Instead of simply wrapping a function into another compartment, - * this helper function creates a native function in the target - * compartment and forwards the call to the original function. - * That call will be different than a regular JS function call in - * that, the |this| is left unbound, and all the non-native JS - * object arguments will be cloned using the structured clone - * algorithm. - * The return value is the new forwarder function, wrapped into - * the caller's compartment. - * The 3rd argument is the name of the property that will - * be set on the target scope, with the forwarder function as - * the value. - * The principal of the caller must subsume that of the target. - * - * Expected type of the arguments and the return value: - * function exportFunction(function funToExport, - * object targetScope, - * string name) - */ -static bool -ExportFunction(JSContext *cx, unsigned argc, jsval *vp) -{ - MOZ_ASSERT(cx); - if (argc < 3) { - JS_ReportError(cx, "Function requires at least 3 arguments"); - return false; - } - - CallArgs args = CallArgsFromVp(argc, vp); - if (!args[0].isObject() || !args[1].isObject() || !args[2].isString()) { - JS_ReportError(cx, "Invalid argument"); - return false; - } - - RootedObject funObj(cx, &args[0].toObject()); - RootedObject targetScope(cx, &args[1].toObject()); - RootedString funName(cx, args[2].toString()); - - // We can only export functions to scopes those are transparent for us, - // so if there is a security wrapper around targetScope we must throw. - targetScope = CheckedUnwrap(targetScope); - if (!targetScope) { - JS_ReportError(cx, "Permission denied to export function into scope"); - return false; - } - - if (JS_GetStringLength(funName) == 0) { - JS_ReportError(cx, "3rd argument should be a non-empty string"); - return false; - } - - { - // We need to operate in the target scope from here on, let's enter - // its compartment. - JSAutoCompartment ac(cx, targetScope); - - // Unwrapping to see if we have a callable. - funObj = UncheckedUnwrap(funObj); - if (!JS_ObjectIsCallable(cx, funObj)) { - JS_ReportError(cx, "First argument must be a function"); - return false; - } - - // The function forwarder will live in the target compartment. Since - // this function will be referenced from its private slot, to avoid a - // GC hazard, we must wrap it to the same compartment. - if (!JS_WrapObject(cx, funObj.address())) - return false; - - RootedId id(cx); - if (!JS_ValueToId(cx, args[2], id.address())) - return false; - - // And now, let's create the forwarder function in the target compartment - // for the function the be exported. - if (!NewFunctionForwarder(cx, id, funObj, /* doclone = */ true, args.rval())) { - JS_ReportError(cx, "Exporting function failed"); - return false; - } - - // We have the forwarder function in the target compartment, now - // we have to add it to the target scope as a property. - if (!JS_DefinePropertyById(cx, targetScope, id, args.rval(), - JS_PropertyStub, JS_StrictPropertyStub, - JSPROP_ENUMERATE)) - return false; - } - - // Finally we have to re-wrap the exported function back to the caller compartment. - if (!JS_WrapValue(cx, args.rval().address())) - return false; - - return true; -} - -static bool -GetFilenameAndLineNumber(JSContext *cx, nsACString &filename, unsigned &lineno) -{ - JSScript *script; - if (JS_DescribeScriptedCaller(cx, &script, &lineno)) { - if (const char *cfilename = JS_GetScriptFilename(cx, script)) { - filename.Assign(nsDependentCString(cfilename)); - return true; - } - } - return false; -} - -namespace xpc { -bool -IsReflector(JSObject *obj) -{ - return IS_WN_REFLECTOR(obj) || dom::IsDOMObject(obj); -} -} /* namespace xpc */ - -enum ForwarderCloneTags { - SCTAG_BASE = JS_SCTAG_USER_MIN, - SCTAG_REFLECTOR -}; - -static JSObject * -CloneNonReflectorsRead(JSContext *cx, JSStructuredCloneReader *reader, uint32_t tag, - uint32_t data, void *closure) -{ - MOZ_ASSERT(closure, "Null pointer!"); - AutoObjectVector *reflectors = static_cast(closure); - if (tag == SCTAG_REFLECTOR) { - MOZ_ASSERT(!data); - - size_t idx; - if (JS_ReadBytes(reader, &idx, sizeof(size_t))) { - RootedObject reflector(cx, reflectors->handleAt(idx)); - MOZ_ASSERT(reflector, "No object pointer?"); - MOZ_ASSERT(IsReflector(reflector), "Object pointer must be a reflector!"); - - JS_WrapObject(cx, reflector.address()); - JS_ASSERT(WrapperFactory::IsXrayWrapper(reflector) || - IsReflector(reflector)); - - return reflector; - } - } - - JS_ReportError(cx, "CloneNonReflectorsRead error"); - return nullptr; -} - -static bool -CloneNonReflectorsWrite(JSContext *cx, JSStructuredCloneWriter *writer, - Handle obj, void *closure) -{ - MOZ_ASSERT(closure, "Null pointer!"); - - // We need to maintain a list of reflectors to make sure all these objects - // are properly rooter. Only their indices will be serialized. - AutoObjectVector *reflectors = static_cast(closure); - if (IsReflector(obj)) { - if (!reflectors->append(obj)) - return false; - - size_t idx = reflectors->length()-1; - if (JS_WriteUint32Pair(writer, SCTAG_REFLECTOR, 0) && - JS_WriteBytes(writer, &idx, sizeof(size_t))) { - return true; - } - } - - JS_ReportError(cx, "CloneNonReflectorsWrite error"); - return false; -} - -JSStructuredCloneCallbacks gForwarderStructuredCloneCallbacks = { - CloneNonReflectorsRead, - CloneNonReflectorsWrite, - nullptr -}; - -/* - * This is a special structured cloning, that clones only non-reflectors. - * The function assumes the cx is already entered the compartment we want - * to clone to, and that if val is an object is from the compartment we - * clone from. - */ -bool -CloneNonReflectors(JSContext *cx, MutableHandleValue val) -{ - JSAutoStructuredCloneBuffer buffer; - AutoObjectVector rootedReflectors(cx); - { - // For parsing val we have to enter its compartment. - // (unless it's a primitive) - Maybe ac; - if (val.isObject()) { - ac.construct(cx, &val.toObject()); - } - - if (!buffer.write(cx, val, - &gForwarderStructuredCloneCallbacks, - &rootedReflectors)) - { - return false; - } - } - - // Now recreate the clones in the target compartment. - RootedValue rval(cx); - if (!buffer.read(cx, val.address(), - &gForwarderStructuredCloneCallbacks, - &rootedReflectors)) - { - return false; - } - - return true; -} - -/* - * Similar to evalInSandbox except this one is used to eval a script in the - * scope of a window. Also note, that the return value and the possible exceptions - * in the script are structured cloned, unless they are natives (then they are just - * wrapped). - * Principal of the caller must subsume the target's. - * - * Expected type of the arguments: - * value evalInWindow(string script, - * object window) - */ -static bool -EvalInWindow(JSContext *cx, unsigned argc, jsval *vp) -{ - MOZ_ASSERT(cx); - if (argc < 2) { - JS_ReportError(cx, "Function requires two arguments"); - return false; - } - - CallArgs args = CallArgsFromVp(argc, vp); - if (!args[0].isString() || !args[1].isObject()) { - JS_ReportError(cx, "Invalid arguments"); - return false; - } - - RootedString srcString(cx, args[0].toString()); - RootedObject targetScope(cx, &args[1].toObject()); - - // If we cannot unwrap we must not eval in it. - targetScope = CheckedUnwrap(targetScope); - if (!targetScope) { - JS_ReportError(cx, "Permission denied to eval in target scope"); - return false; - } - - // Make sure that we have a window object. - RootedObject inner(cx, CheckedUnwrap(targetScope, /* stopAtOuter = */ false)); - nsCOMPtr global; - nsCOMPtr window; - if (!JS_IsGlobalObject(inner) || - !(global = GetNativeForGlobal(inner)) || - !(window = do_QueryInterface(global))) - { - JS_ReportError(cx, "Second argument must be a window"); - return false; - } - - nsCOMPtr context = - (static_cast(window.get()))->GetScriptContext(); - if (!context) { - JS_ReportError(cx, "Script context needed"); - return false; - } - - if (!context->GetScriptsEnabled()) { - JS_ReportError(cx, "Scripts are disabled in this window"); - return false; - } - - nsCString filename; - unsigned lineNo; - if (!GetFilenameAndLineNumber(cx, filename, lineNo)) { - // Default values for non-scripted callers. - filename.Assign("Unknown"); - lineNo = 0; - } - - nsDependentJSString srcDepString; - srcDepString.init(cx, srcString); - - { - // CompileOptions must be created from the context - // we will execute this script in. - JSContext *wndCx = context->GetNativeContext(); - AutoCxPusher pusher(wndCx); - JS::CompileOptions compileOptions(wndCx); - compileOptions.setFileAndLine(filename.get(), lineNo); - - // We don't want the JS engine to automatically report - // uncaught exceptions. - nsJSUtils::EvaluateOptions evaluateOptions; - evaluateOptions.setReportUncaught(false); - - nsresult rv = nsJSUtils::EvaluateString(wndCx, - srcDepString, - targetScope, - compileOptions, - evaluateOptions, - args.rval().address()); - - if (NS_FAILED(rv)) { - // If there was an exception we get it as a return value, if - // the evaluation failed for some other reason, then a default - // exception is raised. - MOZ_ASSERT(!JS_IsExceptionPending(wndCx), - "Exception should be delivered as return value."); - if (args.rval().isUndefined()) { - MOZ_ASSERT(rv == NS_ERROR_OUT_OF_MEMORY); - return false; - } - - // If there was an exception thrown we should set it - // on the calling context. - RootedValue exn(wndCx, args.rval()); - // First we should reset the return value. - args.rval().set(UndefinedValue()); - - // Then clone the exception. - if (CloneNonReflectors(cx, &exn)) - JS_SetPendingException(cx, exn); - - return false; - } - } - - // Let's clone the return value back to the callers compartment. - if (!CloneNonReflectors(cx, args.rval())) { - args.rval().set(UndefinedValue()); - return false; - } - - return true; -} - -static bool -sandbox_enumerate(JSContext *cx, HandleObject obj) -{ - return JS_EnumerateStandardClasses(cx, obj); -} - -static bool -sandbox_resolve(JSContext *cx, HandleObject obj, HandleId id) -{ - bool resolved; - return JS_ResolveStandardClass(cx, obj, id, &resolved); -} - -static void -sandbox_finalize(JSFreeOp *fop, JSObject *obj) -{ - nsIScriptObjectPrincipal *sop = - static_cast(xpc_GetJSPrivate(obj)); - MOZ_ASSERT(sop); - static_cast(sop)->ForgetGlobalObject(); - NS_IF_RELEASE(sop); - DestroyProtoAndIfaceCache(obj); -} - -static bool -sandbox_convert(JSContext *cx, HandleObject obj, JSType type, MutableHandleValue vp) -{ - if (type == JSTYPE_OBJECT) { - vp.set(OBJECT_TO_JSVAL(obj)); - return true; - } - - return JS_ConvertStub(cx, obj, type, vp); -} - -static JSClass SandboxClass = { - "Sandbox", - XPCONNECT_GLOBAL_FLAGS, - JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, - sandbox_enumerate, sandbox_resolve, sandbox_convert, sandbox_finalize, - NULL, NULL, NULL, NULL, TraceXPCGlobal -}; - -static const JSFunctionSpec SandboxFunctions[] = { - JS_FS("dump", SandboxDump, 1,0), - JS_FS("debug", SandboxDebug, 1,0), - JS_FS("importFunction", SandboxImport, 1,0), - JS_FS_END -}; - -/***************************************************************************/ -nsXPCComponents_utils_Sandbox::nsXPCComponents_utils_Sandbox() -{ -} - -nsXPCComponents_utils_Sandbox::~nsXPCComponents_utils_Sandbox() -{ -} - -NS_INTERFACE_MAP_BEGIN(nsXPCComponents_utils_Sandbox) - NS_INTERFACE_MAP_ENTRY(nsIXPCComponents_utils_Sandbox) - NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable) - NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_utils_Sandbox) -NS_INTERFACE_MAP_END_THREADSAFE - -NS_IMPL_ADDREF(nsXPCComponents_utils_Sandbox) -NS_IMPL_RELEASE(nsXPCComponents_utils_Sandbox) - -// We use the nsIXPScriptable macros to generate lots of stuff for us. -#define XPC_MAP_CLASSNAME nsXPCComponents_utils_Sandbox -#define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_utils_Sandbox" -#define XPC_MAP_WANT_CALL -#define XPC_MAP_WANT_CONSTRUCT -#define XPC_MAP_FLAGS 0 -#include "xpc_map_end.h" /* This #undef's the above. */ - -xpc::SandboxProxyHandler xpc::sandboxProxyHandler; - -bool -xpc::IsSandboxPrototypeProxy(JSObject *obj) -{ - return js::IsProxy(obj) && - js::GetProxyHandler(obj) == &xpc::sandboxProxyHandler; -} - -bool -xpc::SandboxCallableProxyHandler::call(JSContext *cx, JS::Handle proxy, - const JS::CallArgs &args) -{ - // We forward the call to our underlying callable. - - // The parent of our proxy is the SandboxProxyHandler proxy - RootedObject sandboxProxy(cx, JS_GetParent(proxy)); - MOZ_ASSERT(js::IsProxy(sandboxProxy) && - js::GetProxyHandler(sandboxProxy) == &xpc::sandboxProxyHandler); - - // The parent of the sandboxProxy is the sandbox global, and the - // target object is the original proto. - RootedObject sandboxGlobal(cx, JS_GetParent(sandboxProxy)); - MOZ_ASSERT(js::GetObjectJSClass(sandboxGlobal) == &SandboxClass); - - // If our this object is the sandbox global, we call with this set to the - // original proto instead. - // - // There are two different ways we can compute |this|. If we use - // JS_THIS_VALUE, we'll get the bonafide |this| value as passed by the - // caller, which may be undefined if a global function was invoked without - // an explicit invocant. If we use JS_THIS or JS_THIS_OBJECT, the |this| - // in |vp| will be coerced to the global, which is not the correct - // behavior in ES5 strict mode. And we have no way to compute strictness - // here. - // - // The naive approach is simply to use JS_THIS_VALUE here. If |this| was - // explicit, we can remap it appropriately. If it was implicit, then we - // leave it as undefined, and let the callee sort it out. Since the callee - // is generally in the same compartment as its global (eg the Window's - // compartment, not the Sandbox's), the callee will generally compute the - // correct |this|. - // - // However, this breaks down in the Xray case. If the sandboxPrototype - // is an Xray wrapper, then we'll end up reifying the native methods in - // the Sandbox's scope, which means that they'll compute |this| to be the - // Sandbox, breaking old-style XPC_WN_CallMethod methods. - // - // Luckily, the intent of Xrays is to provide a vanilla view of a foreign - // DOM interface, which means that we don't care about script-enacted - // strictness in the prototype's home compartment. Indeed, since DOM - // methods are always non-strict, we can just assume non-strict semantics - // if the sandboxPrototype is an Xray Wrapper, which lets us appropriately - // remap |this|. - JS::Value thisVal = - WrapperFactory::IsXrayWrapper(sandboxProxy) ? args.computeThis(cx) : args.thisv(); - if (thisVal == ObjectValue(*sandboxGlobal)) { - thisVal = ObjectValue(*js::GetProxyTargetObject(sandboxProxy)); - } - - return JS::Call(cx, thisVal, js::GetProxyPrivate(proxy), args.length(), args.array(), - args.rval()); -} - -xpc::SandboxCallableProxyHandler xpc::sandboxCallableProxyHandler; - -// Wrap a callable such that if we're called with oldThisObj as the -// "this" we will instead call it with newThisObj as the this. -static JSObject* -WrapCallable(JSContext *cx, JSObject *callable, JSObject *sandboxProtoProxy) -{ - MOZ_ASSERT(JS_ObjectIsCallable(cx, callable)); - // Our proxy is wrapping the callable. So we need to use the - // callable as the private. We use the given sandboxProtoProxy as - // the parent, and our call() hook depends on that. - MOZ_ASSERT(js::IsProxy(sandboxProtoProxy) && - js::GetProxyHandler(sandboxProtoProxy) == - &xpc::sandboxProxyHandler); - - RootedValue priv(cx, ObjectValue(*callable)); - return js::NewProxyObject(cx, &xpc::sandboxCallableProxyHandler, - priv, nullptr, - sandboxProtoProxy, js::ProxyIsCallable); -} - -template -bool BindPropertyOp(JSContext *cx, Op &op, JSPropertyDescriptor *desc, HandleId id, - unsigned attrFlag, HandleObject sandboxProtoProxy) -{ - if (!op) { - return true; - } - - RootedObject func(cx); - if (desc->attrs & attrFlag) { - // Already an object - func = JS_FUNC_TO_DATA_PTR(JSObject *, op); - } else { - // We have an actual property op. For getters, we use 0 - // args, for setters we use 1 arg. - uint32_t args = (attrFlag == JSPROP_GETTER) ? 0 : 1; - RootedObject obj(cx, desc->obj); - func = GeneratePropertyOp(cx, obj, id, args, op); - if (!func) - return false; - } - func = WrapCallable(cx, func, sandboxProtoProxy); - if (!func) - return false; - op = JS_DATA_TO_FUNC_PTR(Op, func.get()); - desc->attrs |= attrFlag; - return true; -} - -extern bool -XPC_WN_Helper_GetProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp); -extern bool -XPC_WN_Helper_SetProperty(JSContext *cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp); - -bool -xpc::SandboxProxyHandler::getPropertyDescriptor(JSContext *cx, - JS::Handle proxy, - JS::Handle id, - JS::MutableHandle desc, - unsigned flags) -{ - JS::RootedObject obj(cx, wrappedObject(proxy)); - - MOZ_ASSERT(js::GetObjectCompartment(obj) == js::GetObjectCompartment(proxy)); - if (!JS_GetPropertyDescriptorById(cx, obj, id, - flags, desc)) - return false; - - if (!desc.object()) - return true; // No property, nothing to do - - // Now fix up the getter/setter/value as needed to be bound to desc->obj - // Don't mess with holder_get and holder_set, though, because those rely on - // the "vp is prefilled with the value in the slot" behavior that property - // ops can in theory rely on, but our property op forwarder doesn't know how - // to make that happen. Since we really only need to rebind the DOM methods - // here, not rebindings holder_get and holder_set is OK. - // - // Similarly, don't mess with XPC_WN_Helper_GetProperty and - // XPC_WN_Helper_SetProperty, for the same reasons: that could confuse our - // access to expandos when we're not doing Xrays. - if (desc.getter() != xpc::holder_get && - desc.getter() != XPC_WN_Helper_GetProperty && - !BindPropertyOp(cx, desc.getter(), desc.address(), id, JSPROP_GETTER, proxy)) - return false; - if (desc.setter() != xpc::holder_set && - desc.setter() != XPC_WN_Helper_SetProperty && - !BindPropertyOp(cx, desc.setter(), desc.address(), id, JSPROP_SETTER, proxy)) - return false; - if (desc.value().isObject()) { - JSObject* val = &desc.value().toObject(); - if (JS_ObjectIsCallable(cx, val)) { - val = WrapCallable(cx, val, proxy); - if (!val) - return false; - desc.value().setObject(*val); - } - } - - return true; -} - -bool -xpc::SandboxProxyHandler::getOwnPropertyDescriptor(JSContext *cx, - JS::Handle proxy, - JS::Handle id, - JS::MutableHandle desc, - unsigned flags) -{ - if (!getPropertyDescriptor(cx, proxy, id, desc, flags)) - return false; - - if (desc.object() != wrappedObject(proxy)) - desc.object().set(nullptr); - - return true; -} - -/* - * Reuse the BaseProxyHandler versions of the derived traps that are implemented - * in terms of the fundamental traps. - */ - -bool -xpc::SandboxProxyHandler::has(JSContext *cx, JS::Handle proxy, - JS::Handle id, bool *bp) -{ - return BaseProxyHandler::has(cx, proxy, id, bp); -} -bool -xpc::SandboxProxyHandler::hasOwn(JSContext *cx, JS::Handle proxy, - JS::Handle id, bool *bp) -{ - return BaseProxyHandler::hasOwn(cx, proxy, id, bp); -} - -bool -xpc::SandboxProxyHandler::get(JSContext *cx, JS::Handle proxy, - JS::Handle receiver, - JS::Handle id, - JS::MutableHandle vp) -{ - return BaseProxyHandler::get(cx, proxy, receiver, id, vp); -} - -bool -xpc::SandboxProxyHandler::set(JSContext *cx, JS::Handle proxy, - JS::Handle receiver, - JS::Handle id, - bool strict, - JS::MutableHandle vp) -{ - return BaseProxyHandler::set(cx, proxy, receiver, id, strict, vp); -} - -bool -xpc::SandboxProxyHandler::keys(JSContext *cx, JS::Handle proxy, - AutoIdVector &props) -{ - return BaseProxyHandler::keys(cx, proxy, props); -} - -bool -xpc::SandboxProxyHandler::iterate(JSContext *cx, JS::Handle proxy, - unsigned flags, JS::MutableHandle vp) -{ - return BaseProxyHandler::iterate(cx, proxy, flags, vp); -} - -nsresult -xpc_CreateSandboxObject(JSContext *cx, jsval *vp, nsISupports *prinOrSop, SandboxOptions& options) -{ - // Create the sandbox global object - nsresult rv; - nsCOMPtr xpc(do_GetService(nsIXPConnect::GetCID(), &rv)); - if (NS_FAILED(rv)) - return NS_ERROR_XPC_UNEXPECTED; - - nsCOMPtr principal = do_QueryInterface(prinOrSop); - if (!principal) { - nsCOMPtr sop = do_QueryInterface(prinOrSop); - if (sop) { - principal = sop->GetPrincipal(); - } else { - principal = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv); - MOZ_ASSERT(NS_FAILED(rv) || principal, "Bad return from do_CreateInstance"); - - if (!principal || NS_FAILED(rv)) { - if (NS_SUCCEEDED(rv)) { - rv = NS_ERROR_FAILURE; - } - - return rv; - } - } - MOZ_ASSERT(principal); - } - - JS::CompartmentOptions compartmentOptions; - compartmentOptions.setZone(options.sameZoneAs - ? JS::SameZoneAs(js::UncheckedUnwrap(options.sameZoneAs)) - : JS::SystemZone); - RootedObject sandbox(cx, xpc::CreateGlobalObject(cx, &SandboxClass, - principal, compartmentOptions)); - if (!sandbox) - return NS_ERROR_FAILURE; - - // Set up the wantXrays flag, which indicates whether xrays are desired even - // for same-origin access. - // - // This flag has historically been ignored for chrome sandboxes due to - // quirks in the wrapping implementation that have now been removed. Indeed, - // same-origin Xrays for chrome->chrome access seems a bit superfluous. - // Arguably we should just flip the default for chrome and still honor the - // flag, but such a change would break code in subtle ways for minimal - // benefit. So we just switch it off here. - xpc::GetCompartmentPrivate(sandbox)->wantXrays = - AccessCheck::isChrome(sandbox) ? false : options.wantXrays; - - { - JSAutoCompartment ac(cx, sandbox); - - if (options.proto) { - bool ok = JS_WrapObject(cx, options.proto.address()); - if (!ok) - return NS_ERROR_XPC_UNEXPECTED; - - if (xpc::WrapperFactory::IsXrayWrapper(options.proto) && !options.wantXrays) { - RootedValue v(cx, ObjectValue(*options.proto)); - if (!xpc::WrapperFactory::WaiveXrayAndWrap(cx, v.address())) - return NS_ERROR_FAILURE; - options.proto = &v.toObject(); - } - - // Now check what sort of thing we've got in |proto| - JSObject *unwrappedProto = js::UncheckedUnwrap(options.proto, false); - js::Class *unwrappedClass = js::GetObjectClass(unwrappedProto); - if (IS_WN_CLASS(unwrappedClass) || - mozilla::dom::IsDOMClass(Jsvalify(unwrappedClass))) { - // Wrap it up in a proxy that will do the right thing in terms - // of this-binding for methods. - RootedValue priv(cx, ObjectValue(*options.proto)); - options.proto = js::NewProxyObject(cx, &xpc::sandboxProxyHandler, - priv, nullptr, sandbox); - if (!options.proto) - return NS_ERROR_OUT_OF_MEMORY; - } - - ok = JS_SetPrototype(cx, sandbox, options.proto); - if (!ok) - return NS_ERROR_XPC_UNEXPECTED; - } - - nsCOMPtr sbp = - new SandboxPrivate(principal, sandbox); - - // Pass on ownership of sbp to |sandbox|. - JS_SetPrivate(sandbox, sbp.forget().get()); - - bool allowComponents = nsContentUtils::IsSystemPrincipal(principal) || - nsContentUtils::IsExpandedPrincipal(principal); - if (options.wantComponents && allowComponents && - !nsXPCComponents::AttachComponentsObject(cx, GetObjectScope(sandbox))) - return NS_ERROR_XPC_UNEXPECTED; - - if (!XPCNativeWrapper::AttachNewConstructorObject(cx, sandbox)) - return NS_ERROR_XPC_UNEXPECTED; - - if (!JS_DefineFunctions(cx, sandbox, SandboxFunctions)) - return NS_ERROR_XPC_UNEXPECTED; - - if (options.wantXHRConstructor && - !JS_DefineFunction(cx, sandbox, "XMLHttpRequest", CreateXMLHttpRequest, 0, JSFUN_CONSTRUCTOR)) - return NS_ERROR_XPC_UNEXPECTED; - - if (options.wantExportHelpers && - (!JS_DefineFunction(cx, sandbox, "exportFunction", ExportFunction, 3, 0) || - !JS_DefineFunction(cx, sandbox, "evalInWindow", EvalInWindow, 2, 0))) - return NS_ERROR_XPC_UNEXPECTED; - - } - - if (vp) { - // We have this crazy behavior where wantXrays=false also implies that the - // returned sandbox is implicitly waived. We've stopped advertising it, but - // keep supporting it for now. - *vp = OBJECT_TO_JSVAL(sandbox); - if (options.wantXrays && !JS_WrapValue(cx, vp)) - return NS_ERROR_UNEXPECTED; - if (!options.wantXrays && !xpc::WrapperFactory::WaiveXrayAndWrap(cx, vp)) - return NS_ERROR_UNEXPECTED; - } - - // Set the location information for the new global, so that tools like - // about:memory may use that information - xpc::SetLocationForGlobal(sandbox, options.sandboxName); - - JS_FireOnNewGlobalObject(cx, sandbox); - - return NS_OK; -} - -/* bool call(in nsIXPConnectWrappedNative wrapper, - in JSContextPtr cx, - in JSObjectPtr obj, - in uint32_t argc, - in JSValPtr argv, - in JSValPtr vp); -*/ -NS_IMETHODIMP -nsXPCComponents_utils_Sandbox::Call(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *objArg, const CallArgs &args, bool *_retval) -{ - RootedObject obj(cx, objArg); - return CallOrConstruct(wrapper, cx, obj, args, _retval); -} - -/* bool construct(in nsIXPConnectWrappedNative wrapper, - in JSContextPtr cx, - in JSObjectPtr obj, - in uint32_t argc, - in JSValPtr argv, - in JSValPtr vp); -*/ -NS_IMETHODIMP -nsXPCComponents_utils_Sandbox::Construct(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *objArg, const CallArgs &args, bool *_retval) -{ - RootedObject obj(cx, objArg); - return CallOrConstruct(wrapper, cx, obj, args, _retval); -} - -// for sandbox constructor the first argument can be a URI string in which case -// we use the related Codebase Principal for the sandbox -nsresult -GetPrincipalFromString(JSContext *cx, HandleString codebase, nsIPrincipal **principal) -{ - MOZ_ASSERT(principal); - MOZ_ASSERT(codebase); - nsCOMPtr uri; - nsDependentJSString codebaseStr; - NS_ENSURE_TRUE(codebaseStr.init(cx, codebase), NS_ERROR_FAILURE); - nsresult rv = NS_NewURI(getter_AddRefs(uri), codebaseStr); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr secman = - do_GetService(kScriptSecurityManagerContractID); - NS_ENSURE_TRUE(secman, NS_ERROR_FAILURE); - - // We could allow passing in the app-id and browser-element info to the - // sandbox constructor. But creating a sandbox based on a string is a - // deprecated API so no need to add features to it. - rv = secman->GetNoAppCodebasePrincipal(uri, principal); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(*principal, NS_ERROR_FAILURE); - - return NS_OK; -} - -// for sandbox constructor the first argument can be a principal object or -// a script object principal (Document, Window) -nsresult -GetPrincipalOrSOP(JSContext *cx, HandleObject from, nsISupports **out) -{ - MOZ_ASSERT(out); - *out = NULL; - - nsXPConnect* xpc = nsXPConnect::XPConnect(); - nsCOMPtr wrapper; - xpc->GetWrappedNativeOfJSObject(cx, from, - getter_AddRefs(wrapper)); - - NS_ENSURE_TRUE(wrapper, NS_ERROR_INVALID_ARG); - - if (nsCOMPtr sop = do_QueryWrappedNative(wrapper)) { - sop.forget(out); - return NS_OK; - } - - nsCOMPtr principal = do_QueryWrappedNative(wrapper); - principal.forget(out); - NS_ENSURE_TRUE(*out, NS_ERROR_INVALID_ARG); - - return NS_OK; -} - -// the first parameter of the sandbox constructor might be an array of principals, either in string -// format or actual objects (see GetPrincipalOrSOP) -nsresult -GetExpandedPrincipal(JSContext *cx, HandleObject arrayObj, nsIExpandedPrincipal **out) -{ - MOZ_ASSERT(out); - uint32_t length; - - if (!JS_IsArrayObject(cx, arrayObj) || - !JS_GetArrayLength(cx, arrayObj, &length) || - !length) - { - // we need a white list of principals or uri strings to create an - // expanded principal, if we got an empty array or something else - // report error - return NS_ERROR_INVALID_ARG; - } - - nsTArray< nsCOMPtr > allowedDomains(length); - allowedDomains.SetLength(length); - nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); - NS_ENSURE_TRUE(ssm, NS_ERROR_XPC_UNEXPECTED); - - for (uint32_t i = 0; i < length; ++i) { - RootedValue allowed(cx); - if (!JS_GetElement(cx, arrayObj, i, &allowed)) - return NS_ERROR_INVALID_ARG; - - nsresult rv; - nsCOMPtr principal; - if (allowed.isString()) { - // in case of string let's try to fetch a codebase principal from it - RootedString str(cx, allowed.toString()); - rv = GetPrincipalFromString(cx, str, getter_AddRefs(principal)); - NS_ENSURE_SUCCESS(rv, rv); - } else if (allowed.isObject()) { - // in case of object let's see if it's a Principal or a ScriptObjectPrincipal - nsCOMPtr prinOrSop; - RootedObject obj(cx, &allowed.toObject()); - rv = GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr sop(do_QueryInterface(prinOrSop)); - principal = do_QueryInterface(prinOrSop); - if (sop) { - principal = sop->GetPrincipal(); - } - } - NS_ENSURE_TRUE(principal, NS_ERROR_INVALID_ARG); - - // We do not allow ExpandedPrincipals to contain any system principals - bool isSystem; - rv = ssm->IsSystemPrincipal(principal, &isSystem); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_FALSE(isSystem, NS_ERROR_INVALID_ARG); - allowedDomains[i] = principal; - } - - nsCOMPtr result = new nsExpandedPrincipal(allowedDomains); - result.forget(out); - return NS_OK; -} - -// helper that tries to get a property form the options object -nsresult -GetPropFromOptions(JSContext *cx, HandleObject from, const char *name, MutableHandleValue prop, - bool *found) -{ - if (!JS_HasProperty(cx, from, name, found)) - return NS_ERROR_INVALID_ARG; - - if (found && !JS_GetProperty(cx, from, name, prop)) - return NS_ERROR_INVALID_ARG; - - return NS_OK; -} - -// helper that tries to get a boolean property form the options object -nsresult -GetBoolPropFromOptions(JSContext *cx, HandleObject from, const char *name, bool *prop) -{ - MOZ_ASSERT(prop); - - - RootedValue value(cx); - bool found; - if (NS_FAILED(GetPropFromOptions(cx, from, name, &value, &found))) - return NS_ERROR_INVALID_ARG; - - if (!found) - return NS_OK; - - if (!value.isBoolean()) - return NS_ERROR_INVALID_ARG; - - *prop = value.toBoolean(); - return NS_OK; -} - -// helper that tries to get an object property form the options object -nsresult -GetObjPropFromOptions(JSContext *cx, HandleObject from, const char *name, JSObject **prop) -{ - MOZ_ASSERT(prop); - - RootedValue value(cx); - bool found; - if (NS_FAILED(GetPropFromOptions(cx, from, name, &value, &found))) - return NS_ERROR_INVALID_ARG; - - if (!found) { - *prop = NULL; - return NS_OK; - } - - if (!value.isObject()) - return NS_ERROR_INVALID_ARG; - - *prop = &value.toObject(); - return NS_OK; -} - -// helper that tries to get a string property form the options object -nsresult -GetStringPropFromOptions(JSContext *cx, HandleObject from, const char *name, nsCString &prop) -{ - RootedValue value(cx); - bool found; - nsresult rv = GetPropFromOptions(cx, from, name, &value, &found); - NS_ENSURE_SUCCESS(rv, rv); - - if (!found) - return NS_OK; - - NS_ENSURE_TRUE(value.isString(), NS_ERROR_INVALID_ARG); - - char *tmp = JS_EncodeString(cx, value.toString()); - NS_ENSURE_TRUE(tmp, NS_ERROR_INVALID_ARG); - prop.Adopt(tmp, strlen(tmp)); - return NS_OK; -} - -// helper that parsing the sandbox options object (from) and sets the fields of the incoming options struct (options) -nsresult -ParseOptionsObject(JSContext *cx, jsval from, SandboxOptions &options) -{ - NS_ENSURE_TRUE(from.isObject(), NS_ERROR_INVALID_ARG); - RootedObject optionsObject(cx, &from.toObject()); - nsresult rv = GetObjPropFromOptions(cx, optionsObject, - "sandboxPrototype", options.proto.address()); - NS_ENSURE_SUCCESS(rv, rv); - - rv = GetBoolPropFromOptions(cx, optionsObject, - "wantXrays", &options.wantXrays); - NS_ENSURE_SUCCESS(rv, rv); - - rv = GetBoolPropFromOptions(cx, optionsObject, - "wantComponents", &options.wantComponents); - NS_ENSURE_SUCCESS(rv, rv); - - rv = GetBoolPropFromOptions(cx, optionsObject, - "wantXHRConstructor", &options.wantXHRConstructor); - NS_ENSURE_SUCCESS(rv, rv); - - rv = GetBoolPropFromOptions(cx, optionsObject, - "wantExportHelpers", &options.wantExportHelpers); - NS_ENSURE_SUCCESS(rv, rv); - - rv = GetStringPropFromOptions(cx, optionsObject, - "sandboxName", options.sandboxName); - NS_ENSURE_SUCCESS(rv, rv); - - rv = GetObjPropFromOptions(cx, optionsObject, - "sameZoneAs", options.sameZoneAs.address()); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -static nsresult -AssembleSandboxMemoryReporterName(JSContext *cx, nsCString &sandboxName) -{ - // Use a default name when the caller did not provide a sandboxName. - if (sandboxName.IsEmpty()) - sandboxName = NS_LITERAL_CSTRING("[anonymous sandbox]"); - - nsXPConnect* xpc = nsXPConnect::XPConnect(); - // Get the xpconnect native call context. - nsAXPCNativeCallContext *cc = nullptr; - xpc->GetCurrentNativeCallContext(&cc); - NS_ENSURE_TRUE(cc, NS_ERROR_INVALID_ARG); - - // Get the current source info from xpc. - nsCOMPtr frame; - xpc->GetCurrentJSStack(getter_AddRefs(frame)); - - // Append the caller's location information. - if (frame) { - nsCString location; - int32_t lineNumber = 0; - frame->GetFilename(getter_Copies(location)); - frame->GetLineNumber(&lineNumber); - - sandboxName.AppendLiteral(" (from: "); - sandboxName.Append(location); - sandboxName.AppendLiteral(":"); - sandboxName.AppendInt(lineNumber); - sandboxName.AppendLiteral(")"); - } - - return NS_OK; -} - -// static -nsresult -nsXPCComponents_utils_Sandbox::CallOrConstruct(nsIXPConnectWrappedNative *wrapper, - JSContext *cx, HandleObject obj, - const CallArgs &args, bool *_retval) -{ - if (args.length() < 1) - return ThrowAndFail(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx, _retval); - - nsresult rv; - - // Make sure to set up principals on the sandbox before initing classes - nsCOMPtr principal; - nsCOMPtr expanded; - nsCOMPtr prinOrSop; - - if (args[0].isString()) { - RootedString str(cx, args[0].toString()); - rv = GetPrincipalFromString(cx, str, getter_AddRefs(principal)); - prinOrSop = principal; - } else if (args[0].isObject()) { - RootedObject obj(cx, &args[0].toObject()); - if (JS_IsArrayObject(cx, obj)) { - rv = GetExpandedPrincipal(cx, obj, getter_AddRefs(expanded)); - prinOrSop = expanded; - } else { - rv = GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop)); - } - } else { - return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); - } - - if (NS_FAILED(rv)) - return ThrowAndFail(rv, cx, _retval); - - SandboxOptions options(cx); - - if (args.length() > 1 && args[1].isObject()) { - if (NS_FAILED(ParseOptionsObject(cx, args[1], options))) - return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); - } - - if (NS_FAILED(AssembleSandboxMemoryReporterName(cx, options.sandboxName))) - return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); - - rv = xpc_CreateSandboxObject(cx, args.rval().address(), prinOrSop, options); - - if (NS_FAILED(rv)) - return ThrowAndFail(rv, cx, _retval); - - *_retval = true; - - return rv; -} - -class ContextHolder : public nsIScriptObjectPrincipal -{ -public: - ContextHolder(JSContext *aOuterCx, HandleObject aSandbox, nsIPrincipal *aPrincipal); - virtual ~ContextHolder(); - - JSContext * GetJSContext() - { - return mJSContext; - } - - nsIPrincipal * GetPrincipal() { return mPrincipal; } - - NS_DECL_ISUPPORTS - -private: - JSContext* mJSContext; - nsCOMPtr mPrincipal; -}; - -NS_IMPL_ISUPPORTS1(ContextHolder, nsIScriptObjectPrincipal) - -ContextHolder::ContextHolder(JSContext *aOuterCx, - HandleObject aSandbox, - nsIPrincipal *aPrincipal) - : mJSContext(JS_NewContext(JS_GetRuntime(aOuterCx), 1024)), - mPrincipal(aPrincipal) -{ - if (mJSContext) { - bool isChrome; - DebugOnly rv = XPCWrapper::GetSecurityManager()-> - IsSystemPrincipal(mPrincipal, &isChrome); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - - JS_SetOptions(mJSContext, - JS_GetOptions(mJSContext) | - JSOPTION_DONT_REPORT_UNCAUGHT | - JSOPTION_PRIVATE_IS_NSISUPPORTS); - js::SetDefaultObjectForContext(mJSContext, aSandbox); - JS_SetContextPrivate(mJSContext, this); - } -} - -ContextHolder::~ContextHolder() -{ - if (mJSContext) - JS_DestroyContextNoGC(mJSContext); -} - -/***************************************************************************/ - /* void evalInSandbox(in AString source, in nativeobj sandbox); */ NS_IMETHODIMP nsXPCComponents_Utils::EvalInSandbox(const nsAString& source, @@ -4203,109 +2840,6 @@ nsXPCComponents_Utils::EvalInSandbox(const nsAString& source, return NS_OK; } -nsresult -xpc_EvalInSandbox(JSContext *cx, HandleObject sandboxArg, const nsAString& source, - const char *filename, int32_t lineNo, - JSVersion jsVersion, bool returnStringOnly, MutableHandleValue rval) -{ - JS_AbortIfWrongThread(JS_GetRuntime(cx)); - rval.set(UndefinedValue()); - - bool waiveXray = xpc::WrapperFactory::HasWaiveXrayFlag(sandboxArg); - RootedObject sandbox(cx, js::CheckedUnwrap(sandboxArg)); - if (!sandbox || js::GetObjectJSClass(sandbox) != &SandboxClass) { - return NS_ERROR_INVALID_ARG; - } - - nsIScriptObjectPrincipal *sop = - (nsIScriptObjectPrincipal*)xpc_GetJSPrivate(sandbox); - MOZ_ASSERT(sop, "Invalid sandbox passed"); - nsCOMPtr prin = sop->GetPrincipal(); - NS_ENSURE_TRUE(prin, NS_ERROR_FAILURE); - - nsAutoCString filenameBuf; - if (!filename) { - // Default to the spec of the principal. - nsJSPrincipals::get(prin)->GetScriptLocation(filenameBuf); - filename = filenameBuf.get(); - lineNo = 1; - } - - // We create a separate cx to do the sandbox evaluation. Scope it. - RootedValue v(cx, UndefinedValue()); - RootedValue exn(cx, UndefinedValue()); - bool ok = true; - { - // Make a special cx for the sandbox and push it. - // NB: As soon as the RefPtr goes away, the cx goes away. So declare - // it first so that it disappears last. - nsRefPtr sandcxHolder = new ContextHolder(cx, sandbox, prin); - JSContext *sandcx = sandcxHolder->GetJSContext(); - if (!sandcx) { - JS_ReportError(cx, "Can't prepare context for evalInSandbox"); - return NS_ERROR_OUT_OF_MEMORY; - } - nsCxPusher pusher; - pusher.Push(sandcx); - JSAutoCompartment ac(sandcx, sandbox); - - JS::CompileOptions options(sandcx); - options.setPrincipals(nsJSPrincipals::get(prin)) - .setFileAndLine(filename, lineNo); - if (jsVersion != JSVERSION_DEFAULT) - options.setVersion(jsVersion); - JS::RootedObject rootedSandbox(sandcx, sandbox); - ok = JS::Evaluate(sandcx, rootedSandbox, options, - PromiseFlatString(source).get(), source.Length(), - v.address()); - if (ok && returnStringOnly && !v.isUndefined()) { - JSString *str = JS_ValueToString(sandcx, v); - ok = !!str; - v = ok ? JS::StringValue(str) : JS::UndefinedValue(); - } - - // If the sandbox threw an exception, grab it off the context. - if (JS_GetPendingException(sandcx, exn.address())) { - MOZ_ASSERT(!ok); - JS_ClearPendingException(sandcx); - if (returnStringOnly) { - // The caller asked for strings only, convert the - // exception into a string. - JSString *str = JS_ValueToString(sandcx, exn); - exn = str ? JS::StringValue(str) : JS::UndefinedValue(); - } - } - } - - // - // Alright, we're back on the caller's cx. If an error occured, try to - // wrap and set the exception. Otherwise, wrap the return value. - // - - if (!ok) { - // If we end up without an exception, it was probably due to OOM along - // the way, in which case we thow. Otherwise, wrap it. - if (exn.isUndefined() || !JS_WrapValue(cx, exn.address())) - return NS_ERROR_OUT_OF_MEMORY; - - // Set the exception on our caller's cx. - JS_SetPendingException(cx, exn); - return NS_ERROR_FAILURE; - } - - // Transitively apply Xray waivers if |sb| was waived. - if (waiveXray) { - ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, v.address()); - } else { - ok = JS_WrapValue(cx, v.address()); - } - NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE); - - // Whew! - rval.set(v); - return NS_OK; -} - /* JSObject import (in AUTF8String registryLocation, * [optional] in JSObject targetObj); */ @@ -4564,73 +3098,6 @@ nsXPCComponents_Utils::CreateDateIn(const Value &vobj, int64_t msec, JSContext * return NS_OK; } -bool -NonCloningFunctionForwarder(JSContext *cx, unsigned argc, Value *vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0)); - MOZ_ASSERT(v.isObject(), "weird function"); - - JSObject *obj = JS_THIS_OBJECT(cx, vp); - if (!obj) { - return false; - } - return JS_CallFunctionValue(cx, obj, v, args.length(), args.array(), vp); -} - -/* - * Forwards the call to the exported function. Clones all the non reflectors, ignores - * the |this| argument. - */ -bool -CloningFunctionForwarder(JSContext *cx, unsigned argc, Value *vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0)); - NS_ASSERTION(v.isObject(), "weird function"); - RootedObject origFunObj(cx, UncheckedUnwrap(&v.toObject())); - { - JSAutoCompartment ac(cx, origFunObj); - // Note: only the arguments are cloned not the |this| or the |callee|. - // Function forwarder does not use those. - for (unsigned i = 0; i < args.length(); i++) { - if (!CloneNonReflectors(cx, args[i])) { - return false; - } - } - - // JS API does not support any JSObject to JSFunction conversion, - // so let's use JS_CallFunctionValue instead. - RootedValue functionVal(cx); - functionVal.setObject(*origFunObj); - - if (!JS_CallFunctionValue(cx, nullptr, functionVal, args.length(), args.array(), vp)) - return false; - } - - // Return value must be wrapped. - return JS_WrapValue(cx, vp); -} - -bool -NewFunctionForwarder(JSContext *cx, HandleId id, HandleObject callable, bool doclone, - MutableHandleValue vp) -{ - JSFunction *fun = js::NewFunctionByIdWithReserved(cx, doclone ? CloningFunctionForwarder : - NonCloningFunctionForwarder, - 0,0, JS::CurrentGlobalOrNull(cx), id); - - if (!fun) - return false; - - JSObject *funobj = JS_GetFunctionObject(fun); - js::SetFunctionNativeReserved(funobj, 0, ObjectValue(*callable)); - vp.setObject(*funobj); - return true; -} - /* void makeObjectPropsNormal(jsval vobj); */ NS_IMETHODIMP nsXPCComponents_Utils::MakeObjectPropsNormal(const Value &vobj, JSContext *cx) @@ -4860,7 +3327,7 @@ nsXPCComponents_Utils::NukeSandbox(const JS::Value &obj, JSContext *cx) JSObject *wrapper = &obj.toObject(); NS_ENSURE_TRUE(IsWrapper(wrapper), NS_ERROR_INVALID_ARG); JSObject *sb = UncheckedUnwrap(wrapper); - NS_ENSURE_TRUE(GetObjectJSClass(sb) == &SandboxClass, NS_ERROR_INVALID_ARG); + NS_ENSURE_TRUE(IsSandbox(sb), NS_ERROR_INVALID_ARG); NukeCrossCompartmentWrappers(cx, AllCompartments(), SingleCompartment(GetObjectCompartment(sb)), NukeWindowReferences); diff --git a/js/xpconnect/src/moz.build b/js/xpconnect/src/moz.build index 333faf7130fc..f67614c3673a 100644 --- a/js/xpconnect/src/moz.build +++ b/js/xpconnect/src/moz.build @@ -51,5 +51,6 @@ CPP_SOURCES += [ 'nsCxPusher.cpp', 'nsScriptError.cpp', 'nsXPConnect.cpp', + 'Sandbox.cpp', ] diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 9042a47fb210..8fa989ab3fa9 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -3681,7 +3681,22 @@ xpc_GetSafeJSContext() return XPCJSRuntime::Get()->GetJSContextStack()->GetSafeJSContext(); } +bool +NewFunctionForwarder(JSContext *cx, JS::HandleId id, JS::HandleObject callable, + bool doclone, JS::MutableHandleValue vp); + +nsresult +ThrowAndFail(nsresult errNum, JSContext* cx, bool* retval); + +// Infallible. +already_AddRefed +NewSandboxConstructor(); + +bool +IsSandbox(JSObject *obj); + namespace xpc { + struct SandboxOptions { SandboxOptions(JSContext *cx) : wantXrays(true) From 69f8df81a436faa93761de44e85948f73abea913 Mon Sep 17 00:00:00 2001 From: Christoph Kerschbaumer Date: Mon, 19 Aug 2013 10:20:20 -0700 Subject: [PATCH 07/54] Bug 902156 - Persist 'disable protection' option for Mixed Content Blocker. r=smaug, tanvi --- docshell/base/nsDocShell.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index eae632896218..bb2a03a0fc2d 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -9552,9 +9552,20 @@ nsDocShell::DoURILoad(nsIURI * aURI, if (mLoadType == LOAD_RELOAD_ALLOW_MIXED_CONTENT) { rv = SetMixedContentChannel(channel); NS_ENSURE_SUCCESS(rv, rv); - } else { - rv = SetMixedContentChannel(nullptr); - NS_ENSURE_SUCCESS(rv, rv); + } else if (mMixedContentChannel) { + /* + * If the user "Disables Protection on This Page", we call + * SetMixedContentChannel for the first time, otherwise + * mMixedContentChannel is still null. + * Later, if the new channel passes a same orign check, we remember the + * users decision by calling SetMixedContentChannel using the new channel. + * This way, the user does not have to click the disable protection button + * over and over for browsing the same site. + */ + rv = nsContentUtils::CheckSameOrigin(mMixedContentChannel, channel); + if (NS_FAILED(rv) || NS_FAILED(SetMixedContentChannel(channel))) { + SetMixedContentChannel(nullptr); + } } //hack From f8ccf90d6b5f71e568aaacf015ca1b51f138361e Mon Sep 17 00:00:00 2001 From: Christoph Kerschbaumer Date: Fri, 16 Aug 2013 22:15:02 -0700 Subject: [PATCH 08/54] Bug 902156 - Mochitest for Persist 'disable protection' option for Mixed Content Blocker. r=smaug, tanvi --- browser/base/content/test/Makefile.in | 5 + .../base/content/test/browser_bug902156.js | 213 ++++++++++++++++++ browser/base/content/test/file_bug902156.js | 5 + .../base/content/test/file_bug902156_1.html | 15 ++ .../base/content/test/file_bug902156_2.html | 17 ++ .../base/content/test/file_bug902156_3.html | 15 ++ 6 files changed, 270 insertions(+) create mode 100644 browser/base/content/test/browser_bug902156.js create mode 100644 browser/base/content/test/file_bug902156.js create mode 100644 browser/base/content/test/file_bug902156_1.html create mode 100644 browser/base/content/test/file_bug902156_2.html create mode 100644 browser/base/content/test/file_bug902156_3.html diff --git a/browser/base/content/test/Makefile.in b/browser/base/content/test/Makefile.in index cefbe50bfdd5..0e3174f6a6ef 100644 --- a/browser/base/content/test/Makefile.in +++ b/browser/base/content/test/Makefile.in @@ -185,6 +185,7 @@ MOCHITEST_BROWSER_FILES = \ browser_bug818118.js \ browser_bug820497.js \ browser_bug822367.js \ + browser_bug902156.js \ browser_bug832435.js \ browser_bug839103.js \ browser_bug880101.js \ @@ -299,6 +300,10 @@ MOCHITEST_BROWSER_FILES = \ file_bug822367_4B.html \ file_bug822367_5.html \ file_bug822367_6.html \ + file_bug902156_1.html \ + file_bug902156_2.html \ + file_bug902156_3.html \ + file_bug902156.js \ file_fullscreen-window-open.html \ healthreport_testRemoteCommands.html \ moz.png \ diff --git a/browser/base/content/test/browser_bug902156.js b/browser/base/content/test/browser_bug902156.js new file mode 100644 index 000000000000..d08f30b097dd --- /dev/null +++ b/browser/base/content/test/browser_bug902156.js @@ -0,0 +1,213 @@ +/* + * Description of the Tests for + * - Bug 902156: Persist "disable protection" option for Mixed Content Blocker + * + * 1. Navigate to the same domain via document.location + * - Load a html page which has mixed content + * - Doorhanger to disable protection appears - we disable it + * - Load a new page from the same origin using document.location + * - Doorhanger should not appear anymore! + * + * 2. Navigate to the same domain via simulateclick for a link on the page + * - Load a html page which has mixed content + * - Doorhanger to disable protection appears - we disable it + * - Load a new page from the same origin simulating a click + * - Doorhanger should not appear anymore! + * + * 3. Navigate to a differnet domain and show the content is still blocked + * - Load a different html page which has mixed content + * - Doorhanger to disable protection should appear again because + * we navigated away from html page where we disabled the protection. + * + * Note, for all tests we set gHttpTestRoot to use 'https'. + */ + +const PREF_ACTIVE = "security.mixed_content.block_active_content"; + +// We alternate for even and odd test cases to simulate different hosts +const gHttpTestRoot1 = "https://test1.example.com/browser/browser/base/content/test/"; +const gHttpTestRoot2 = "https://test2.example.com/browser/browser/base/content/test/"; + +var origBlockActive; +var gTestBrowser = null; + +registerCleanupFunction(function() { + // Set preferences back to their original values + Services.prefs.setBoolPref(PREF_ACTIVE, origBlockActive); +}); + +function cleanUpAfterTests() { + gBrowser.removeCurrentTab(); + window.focus(); + finish(); +} +/* + * Whenever we disable the Mixed Content Blocker of the page + * we have to make sure that our condition is properly loaded. + */ +function waitForCondition(condition, nextTest, errorMsg) { + var tries = 0; + var interval = setInterval(function() { + if (tries >= 30) { + ok(false, errorMsg); + moveOn(); + } + if (condition()) { + moveOn(); + } + tries++; + }, 100); + var moveOn = function() { + clearInterval(interval); nextTest(); + }; +} + +//------------------------ Test 1 ------------------------------ + +function test1A() { + // Removing EventListener because we have to register a new + // one once the page is loaded with mixed content blocker disabled + gTestBrowser.removeEventListener("load", test1A, true); + gTestBrowser.addEventListener("load", test1B, true); + + var notification = PopupNotifications.getNotification("mixed-content-blocked", gTestBrowser); + ok(notification, "OK: Mixed Content Doorhanger appeared in Test1A!"); + + // Disable Mixed Content Protection for the page + notification.secondaryActions[0].callback(); +} + +function test1B() { + var expected = "Mixed Content Blocker disabled"; + waitForCondition( + function() content.document.getElementById('mctestdiv').innerHTML == expected, + test1C, "Error: Waited too long for mixed script to run in Test 1B"); +} + +function test1C() { + gTestBrowser.removeEventListener("load", test1B, true); + var actual = content.document.getElementById('mctestdiv').innerHTML; + is(actual, "Mixed Content Blocker disabled", "OK: Executed mixed script in Test 1C"); + + // The Script loaded after we disabled the page, now we are going to reload the + // page and see if our decision is persistent + gTestBrowser.addEventListener("load", test1D, true); + + var url = gHttpTestRoot1 + "file_bug902156_2.html"; + gTestBrowser.contentWindow.location = url; +} + +function test1D() { + gTestBrowser.removeEventListener("load", test1D, true); + + // The Doorhanger should not appear, because our decision of disabling the + // mixed content blocker is persistent. + var notification = PopupNotifications.getNotification("mixed-content-blocked", gTestBrowser); + ok(!notification, "OK: Mixed Content Doorhanger did not appear again in Test1D!"); + + var actual = content.document.getElementById('mctestdiv').innerHTML; + is(actual, "Mixed Content Blocker disabled", "OK: Executed mixed script in Test 1D"); + + // move on to Test 2 + test2(); +} + +//------------------------ Test 2 ------------------------------ + +function test2() { + gTestBrowser.addEventListener("load", test2A, true); + var url = gHttpTestRoot2 + "file_bug902156_2.html"; + gTestBrowser.contentWindow.location = url; +} + +function test2A() { + // Removing EventListener because we have to register a new + // one once the page is loaded with mixed content blocker disabled + gTestBrowser.removeEventListener("load", test2A, true); + gTestBrowser.addEventListener("load", test2B, true); + + var notification = PopupNotifications.getNotification("mixed-content-blocked", gTestBrowser); + ok(notification, "OK: Mixed Content Doorhanger appeared in Test 2A!"); + + // Disable Mixed Content Protection for the page + notification.secondaryActions[0].callback(); +} + +function test2B() { + var expected = "Mixed Content Blocker disabled"; + waitForCondition( + function() content.document.getElementById('mctestdiv').innerHTML == expected, + test2C, "Error: Waited too long for mixed script to run in Test 2B"); +} + +function test2C() { + gTestBrowser.removeEventListener("load", test2B, true); + var actual = content.document.getElementById('mctestdiv').innerHTML; + is(actual, "Mixed Content Blocker disabled", "OK: Executed mixed script in Test 2C"); + + // The Script loaded after we disabled the page, now we are going to reload the + // page and see if our decision is persistent + gTestBrowser.addEventListener("load", test2D, true); + + // reload the page using the provided link in the html file + var mctestlink = content.document.getElementById("mctestlink"); + mctestlink.click(); +} + +function test2D() { + gTestBrowser.removeEventListener("load", test2D, true); + + // The Doorhanger should not appear, because our decision of disabling the + // mixed content blocker is persistent. + var notification = PopupNotifications.getNotification("mixed-content-blocked", gTestBrowser); + ok(!notification, "OK: Mixed Content Doorhanger did not appear again in Test2D!"); + + var actual = content.document.getElementById('mctestdiv').innerHTML; + is(actual, "Mixed Content Blocker disabled", "OK: Executed mixed script in Test 2D"); + + // move on to Test 3 + test3(); +} + +//------------------------ Test 3 ------------------------------ + +function test3() { + gTestBrowser.addEventListener("load", test3A, true); + var url = gHttpTestRoot1 + "file_bug902156_3.html"; + gTestBrowser.contentWindow.location = url; +} + +function test3A() { + // Removing EventListener because we have to register a new + // one once the page is loaded with mixed content blocker disabled + gTestBrowser.removeEventListener("load", test3A, true); + + var notification = PopupNotifications.getNotification("mixed-content-blocked", gTestBrowser); + ok(notification, "OK: Mixed Content Doorhanger appeared in Test 3A!"); + + // We are done with tests, clean up + cleanUpAfterTests(); +} + +//------------------------------------------------------ + +function test() { + // Performing async calls, e.g. 'onload', we have to wait till all of them finished + waitForExplicitFinish(); + + // Store original preferences so we can restore settings after testing + origBlockActive = Services.prefs.getBoolPref(PREF_ACTIVE); + + Services.prefs.setBoolPref(PREF_ACTIVE, true); + + // Not really sure what this is doing + var newTab = gBrowser.addTab(); + gBrowser.selectedTab = newTab; + gTestBrowser = gBrowser.selectedBrowser; + newTab.linkedBrowser.stop() + + // Starting Test Number 1: + gTestBrowser.addEventListener("load", test1A, true); + var url = gHttpTestRoot1 + "file_bug902156_1.html"; + gTestBrowser.contentWindow.location = url; +} diff --git a/browser/base/content/test/file_bug902156.js b/browser/base/content/test/file_bug902156.js new file mode 100644 index 000000000000..806667204105 --- /dev/null +++ b/browser/base/content/test/file_bug902156.js @@ -0,0 +1,5 @@ +/* + * Once the mixed content blocker is disabled for the page, this scripts loads + * and updates the text inside the div container. + */ +document.getElementById("mctestdiv").innerHTML = "Mixed Content Blocker disabled"; diff --git a/browser/base/content/test/file_bug902156_1.html b/browser/base/content/test/file_bug902156_1.html new file mode 100644 index 000000000000..04d525817565 --- /dev/null +++ b/browser/base/content/test/file_bug902156_1.html @@ -0,0 +1,15 @@ + + + + + + Test 1 for Bug 902156 + + +
Mixed Content Blocker enabled
+ + + diff --git a/browser/base/content/test/file_bug902156_2.html b/browser/base/content/test/file_bug902156_2.html new file mode 100644 index 000000000000..396142520989 --- /dev/null +++ b/browser/base/content/test/file_bug902156_2.html @@ -0,0 +1,17 @@ + + + + + + Test 2 for Bug 902156 + + +
Mixed Content Blocker enabled
+ Go to http site + + + diff --git a/browser/base/content/test/file_bug902156_3.html b/browser/base/content/test/file_bug902156_3.html new file mode 100644 index 000000000000..23d1b11baf92 --- /dev/null +++ b/browser/base/content/test/file_bug902156_3.html @@ -0,0 +1,15 @@ + + + + + + Test 3 for Bug 902156 + + +
Mixed Content Blocker enabled
+ + + From 03e39ee3c3642250cf0fb94f2404c3719eecff3a Mon Sep 17 00:00:00 2001 From: Christoph Kerschbaumer Date: Fri, 16 Aug 2013 23:37:15 -0700 Subject: [PATCH 09/54] Bug 902156 - Persist 'disable protection' option for Mixed Content Blocker - patch requires update for testcase from bug 822367. r=smaug, tanvi --- browser/base/content/test/browser_bug822367.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/browser/base/content/test/browser_bug822367.js b/browser/base/content/test/browser_bug822367.js index ba360a39482c..5ce497919b1f 100644 --- a/browser/base/content/test/browser_bug822367.js +++ b/browser/base/content/test/browser_bug822367.js @@ -5,7 +5,11 @@ const PREF_DISPLAY = "security.mixed_content.block_display_content"; const PREF_ACTIVE = "security.mixed_content.block_active_content"; + +// We alternate for even and odd test cases to simulate different hosts const gHttpTestRoot = "https://example.com/browser/browser/base/content/test/"; +const gHttpTestRoot2 = "https://test1.example.com/browser/browser/base/content/test/"; + var origBlockDisplay; var origBlockActive; var gTestBrowser = null; @@ -62,7 +66,7 @@ function MixedTest1C() { //Mixed Display Test - Doorhanger should not appear function MixedTest2() { gTestBrowser.addEventListener("load", MixedTest2A, true); - var url = gHttpTestRoot + "file_bug822367_2.html"; + var url = gHttpTestRoot2 + "file_bug822367_2.html"; gTestBrowser.contentWindow.location = url; } @@ -102,7 +106,7 @@ function MixedTest3D() { function MixedTest4() { gTestBrowser.removeEventListener("load", MixedTest3B, true); gTestBrowser.addEventListener("load", MixedTest4A, true); - var url = gHttpTestRoot + "file_bug822367_4.html"; + var url = gHttpTestRoot2 + "file_bug822367_4.html"; gTestBrowser.contentWindow.location = url; } function MixedTest4A() { @@ -152,7 +156,7 @@ function MixedTest5C() { function MixedTest6() { gTestBrowser.removeEventListener("load", MixedTest5B, true); gTestBrowser.addEventListener("load", MixedTest6A, true); - var url = gHttpTestRoot + "file_bug822367_6.html"; + var url = gHttpTestRoot2 + "file_bug822367_6.html"; gTestBrowser.contentWindow.location = url; } function MixedTest6A() { From c711c9a9940458b3ea6af496cd1f2b7030f1e14a Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Thu, 22 Aug 2013 12:11:45 +1000 Subject: [PATCH 10/54] Bug 907350 - set cache-control: no-store to avoid browser_thumbnails_background orange. r=adw --- toolkit/components/thumbnails/test/thumbnails_background.sjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/components/thumbnails/test/thumbnails_background.sjs b/toolkit/components/thumbnails/test/thumbnails_background.sjs index fe7e77508a59..e20ad760cb83 100644 --- a/toolkit/components/thumbnails/test/thumbnails_background.sjs +++ b/toolkit/components/thumbnails/test/thumbnails_background.sjs @@ -8,7 +8,7 @@ var timer; function handleRequest(req, resp) { resp.processAsync(); - resp.setHeader("Cache-Control", "no-cache", false); + resp.setHeader("Cache-Control", "no-cache, no-store", false); resp.setHeader("Content-Type", "text/html;charset=utf-8", false); let opts = {}; From 8b2b5b6f7cc5a08ec531d5f178084d11a5e16d76 Mon Sep 17 00:00:00 2001 From: Justin Dolske Date: Wed, 21 Aug 2013 20:09:45 -0700 Subject: [PATCH 11/54] Bug 355063 - bustage followup for Android/Metro. r=sparky --- browser/metro/base/content/contenthandlers/Content.js | 5 +++++ mobile/android/chrome/content/browser.js | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/browser/metro/base/content/contenthandlers/Content.js b/browser/metro/base/content/contenthandlers/Content.js index 0f39bee82b3e..cc5051c050ff 100644 --- a/browser/metro/base/content/contenthandlers/Content.js +++ b/browser/metro/base/content/contenthandlers/Content.js @@ -140,6 +140,7 @@ let Content = { addEventListener("MozApplicationManifest", this, false); addEventListener("DOMContentLoaded", this, false); addEventListener("DOMAutoComplete", this, false); + addEventListener("DOMFormHasPassword", this, false); addEventListener("blur", this, false); addEventListener("pagehide", this, false); // Attach a listener to watch for "click" events bubbling up from error @@ -189,6 +190,10 @@ let Content = { this._onClickCapture(aEvent); break; + case "DOMFormHasPassword": + LoginManagerContent.onFormPassword(aEvent); + break; + case "DOMContentLoaded": LoginManagerContent.onContentLoaded(aEvent); this._maybeNotifyErrorPage(); diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 1dba085b70bb..6c034bc7d7b7 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -2670,6 +2670,7 @@ Tab.prototype = { this.browser.sessionHistory.addSHistoryListener(this); this.browser.addEventListener("DOMContentLoaded", this, true); + this.browser.addEventListener("DOMFormHasPassword", this, true); this.browser.addEventListener("DOMLinkAdded", this, true); this.browser.addEventListener("DOMTitleChanged", this, true); this.browser.addEventListener("DOMWindowClose", this, true); @@ -2818,6 +2819,7 @@ Tab.prototype = { this.browser.sessionHistory.removeSHistoryListener(this); this.browser.removeEventListener("DOMContentLoaded", this, true); + this.browser.removeEventListener("DOMFormHasPassword", this, true); this.browser.removeEventListener("DOMLinkAdded", this, true); this.browser.removeEventListener("DOMTitleChanged", this, true); this.browser.removeEventListener("DOMWindowClose", this, true); @@ -3375,6 +3377,11 @@ Tab.prototype = { break; } + case "DOMFormHasPassword": { + LoginManagerContent.onFormPassword(aEvent); + break; + } + case "DOMLinkAdded": { let target = aEvent.originalTarget; if (!target.href || target.disabled) From a84a0b5f89b0b5ff3dfcc0dcb658982c74997d42 Mon Sep 17 00:00:00 2001 From: Tim Taubert Date: Thu, 22 Aug 2013 09:22:16 +0200 Subject: [PATCH 12/54] Backed out changeset 91d27f0ac7dd (bug 355063) --- browser/metro/base/content/contenthandlers/Content.js | 5 ----- mobile/android/chrome/content/browser.js | 7 ------- 2 files changed, 12 deletions(-) diff --git a/browser/metro/base/content/contenthandlers/Content.js b/browser/metro/base/content/contenthandlers/Content.js index cc5051c050ff..0f39bee82b3e 100644 --- a/browser/metro/base/content/contenthandlers/Content.js +++ b/browser/metro/base/content/contenthandlers/Content.js @@ -140,7 +140,6 @@ let Content = { addEventListener("MozApplicationManifest", this, false); addEventListener("DOMContentLoaded", this, false); addEventListener("DOMAutoComplete", this, false); - addEventListener("DOMFormHasPassword", this, false); addEventListener("blur", this, false); addEventListener("pagehide", this, false); // Attach a listener to watch for "click" events bubbling up from error @@ -190,10 +189,6 @@ let Content = { this._onClickCapture(aEvent); break; - case "DOMFormHasPassword": - LoginManagerContent.onFormPassword(aEvent); - break; - case "DOMContentLoaded": LoginManagerContent.onContentLoaded(aEvent); this._maybeNotifyErrorPage(); diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 6c034bc7d7b7..1dba085b70bb 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -2670,7 +2670,6 @@ Tab.prototype = { this.browser.sessionHistory.addSHistoryListener(this); this.browser.addEventListener("DOMContentLoaded", this, true); - this.browser.addEventListener("DOMFormHasPassword", this, true); this.browser.addEventListener("DOMLinkAdded", this, true); this.browser.addEventListener("DOMTitleChanged", this, true); this.browser.addEventListener("DOMWindowClose", this, true); @@ -2819,7 +2818,6 @@ Tab.prototype = { this.browser.sessionHistory.removeSHistoryListener(this); this.browser.removeEventListener("DOMContentLoaded", this, true); - this.browser.removeEventListener("DOMFormHasPassword", this, true); this.browser.removeEventListener("DOMLinkAdded", this, true); this.browser.removeEventListener("DOMTitleChanged", this, true); this.browser.removeEventListener("DOMWindowClose", this, true); @@ -3377,11 +3375,6 @@ Tab.prototype = { break; } - case "DOMFormHasPassword": { - LoginManagerContent.onFormPassword(aEvent); - break; - } - case "DOMLinkAdded": { let target = aEvent.originalTarget; if (!target.href || target.disabled) From 56276696863402914ece14a510cbac2084f91117 Mon Sep 17 00:00:00 2001 From: Tim Taubert Date: Thu, 22 Aug 2013 09:22:18 +0200 Subject: [PATCH 13/54] Backed out changeset 42be7b929812 (bug 355063) --- browser/base/content/content.js | 1 - modules/libpref/src/init/all.js | 1 - .../passwordmgr/LoginManagerContent.jsm | 70 +------------------ .../components/passwordmgr/test/Makefile.in | 1 - ...est_basic_form_observer_autofillForms.html | 59 ++++++++-------- .../test/test_basic_form_pwevent.html | 66 ----------------- .../passwordmgr/test/test_maxforms_1.html | 7 +- .../passwordmgr/test/test_maxforms_2.html | 7 +- .../passwordmgr/test/test_maxforms_3.html | 7 +- 9 files changed, 35 insertions(+), 184 deletions(-) delete mode 100644 toolkit/components/passwordmgr/test/test_basic_form_pwevent.html diff --git a/browser/base/content/content.js b/browser/base/content/content.js index 824b29a2ac4d..c75a8a40e353 100644 --- a/browser/base/content/content.js +++ b/browser/base/content/content.js @@ -48,7 +48,6 @@ if (Services.prefs.getBoolPref("browser.tabs.remote")) { }); addEventListener("DOMFormHasPassword", function(event) { InsecurePasswordUtils.checkForInsecurePasswords(event.target); - LoginManagerContent.onFormPassword(event); }); addEventListener("DOMAutoComplete", function(event) { LoginManagerContent.onUsernameInput(event); diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index ea9f78fb52f1..98ed61d1ee8f 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -3909,7 +3909,6 @@ pref("signon.SignonFileName3", "signons3.txt"); // obsolete pref("signon.autofillForms", true); pref("signon.autologin.proxy", false); pref("signon.debug", false); -pref("signon.useDOMFormHasPassword", true); // Satchel (Form Manager) prefs pref("browser.formfill.debug", false); diff --git a/toolkit/components/passwordmgr/LoginManagerContent.jsm b/toolkit/components/passwordmgr/LoginManagerContent.jsm index f22bacf9bee8..75f24cee19e2 100644 --- a/toolkit/components/passwordmgr/LoginManagerContent.jsm +++ b/toolkit/components/passwordmgr/LoginManagerContent.jsm @@ -13,8 +13,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); -var gEnabled = false, gDebug = false, gAutofillForms = true; // these mirror signon.* prefs -var gUseDOMFormHasPassword = false; // use DOMFormHasPassword event for autofill +var gEnabled = false, gDebug = false; // these mirror signon.* prefs function log(...pieces) { function generateLogMessage(args) { @@ -71,8 +70,6 @@ var observer = { onPrefChange : function() { gDebug = Services.prefs.getBoolPref("signon.debug"); gEnabled = Services.prefs.getBoolPref("signon.rememberSignons"); - gAutofillForms = Services.prefs.getBoolPref("signon.autofillForms"); - gUseDOMFormHasPassword = Services.prefs.getBoolPref("signon.useDOMFormHasPassword"); }, }; @@ -95,10 +92,6 @@ var LoginManagerContent = { }, onContentLoaded : function (event) { - // If we're using the new DOMFormHasPassword event, don't fill at pageload. - if (gUseDOMFormHasPassword) - return; - if (!event.isTrusted) return; @@ -115,64 +108,6 @@ var LoginManagerContent = { }, - onFormPassword: function (event) { - // If we're not using the new DOMFormHasPassword event, only fill at pageload. - if (!gUseDOMFormHasPassword) - return; - - if (!event.isTrusted) - return; - - if (!gEnabled) - return; - - let form = event.target; - let doc = form.ownerDocument; - - log("onFormPassword for", doc.documentURI); - - // If there are no logins for this site, bail out now. - let formOrigin = LoginUtils._getPasswordOrigin(doc.documentURI); - if (!Services.logins.countLogins(formOrigin, "", null)) - return; - - // If we're currently displaying a master password prompt, defer - // processing this form until the user handles the prompt. - if (Services.logins.uiBusy) { - log("deferring onFormPassword for", doc.documentURI); - let self = this; - let observer = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]), - - observe: function (subject, topic, data) { - log("Got deferred onFormPassword notification:", topic); - // Only run observer once. - Services.obs.removeObserver(this, "passwordmgr-crypto-login"); - Services.obs.removeObserver(this, "passwordmgr-crypto-loginCanceled"); - if (topic == "passwordmgr-crypto-loginCanceled") - return; - self.onFormPassword(event); - }, - handleEvent : function (event) { - // Not expected to be called - } - }; - // Trickyness follows: We want an observer, but don't want it to - // cause leaks. So add the observer with a weak reference, and use - // a dummy event listener (a strong reference) to keep it alive - // until the form is destroyed. - Services.obs.addObserver(observer, "passwordmgr-crypto-login", true); - Services.obs.addObserver(observer, "passwordmgr-crypto-loginCanceled", true); - form.addEventListener("mozCleverClosureHack", observer); - return; - } - - let autofillForm = gAutofillForms && !PrivateBrowsingUtils.isWindowPrivate(doc.defaultView); - - this._fillForm(form, autofillForm, false, false, null); - }, - - /* * onUsernameInput * @@ -602,7 +537,8 @@ var LoginManagerContent = { log("fillDocument processing", forms.length, "forms on", doc.documentURI); - var autofillForm = gAutofillForms && !PrivateBrowsingUtils.isWindowPrivate(doc.defaultView); + var autofillForm = !PrivateBrowsingUtils.isWindowPrivate(doc.defaultView) && + Services.prefs.getBoolPref("signon.autofillForms"); var previousActionOrigin = null; var foundLogins = null; diff --git a/toolkit/components/passwordmgr/test/Makefile.in b/toolkit/components/passwordmgr/test/Makefile.in index 0852f8bf88ad..1eced10a9d61 100644 --- a/toolkit/components/passwordmgr/test/Makefile.in +++ b/toolkit/components/passwordmgr/test/Makefile.in @@ -27,7 +27,6 @@ MOCHITEST_FILES = \ test_basic_form_observer_autocomplete.html \ test_basic_form_observer_foundLogins.html \ test_basic_form_pwonly.html \ - test_basic_form_pwevent.html \ test_bug_227640.html \ test_bug_242956.html \ test_bug_360493_1.html \ diff --git a/toolkit/components/passwordmgr/test/test_basic_form_observer_autofillForms.html b/toolkit/components/passwordmgr/test/test_basic_form_observer_autofillForms.html index 552b7930eccb..199d810671f8 100644 --- a/toolkit/components/passwordmgr/test/test_basic_form_observer_autofillForms.html +++ b/toolkit/components/passwordmgr/test/test_basic_form_observer_autofillForms.html @@ -21,36 +21,6 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm"); // Assume that the pref starts out true, so set to false SpecialPowers.setBoolPref("signon.autofillForms", false); - -var TestObserver = { - receivedNotificationFoundForm : false, - receivedNotificationFoundLogins : false, - dataFoundForm : "", - dataFoundLogins : null, - QueryInterface : XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]), - observe : function (subject, topic, data) { - netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); - var pwmgr = Cc["@mozilla.org/login-manager;1"]. - getService(Ci.nsILoginManager); - if (topic == "passwordmgr-found-form") { - info("got passwordmgr-found-form"); - this.receivedNotificationFoundForm = true; - this.dataFoundForm = data; - // Now fill the form - pwmgr.fillForm(subject); - } else if (topic == "passwordmgr-found-logins") { - info("got passwordmgr-found-logins"); - this.receivedNotificationFoundLogins = true; - this.dataFoundLogins = subject.QueryInterface(Ci.nsIPropertyBag2); - } - } -}; - -// Add the observer -var os = Cc["@mozilla.org/observer-service;1"]. - getService(Ci.nsIObserverService); -os.addObserver(TestObserver, "passwordmgr-found-form", false); -os.addObserver(TestObserver, "passwordmgr-found-logins", false);

@@ -72,6 +42,35 @@ os.addObserver(TestObserver, "passwordmgr-found-logins", false); - - - - - -Mozilla Bug 355063 -

-
-forms go here! -
-
-
- - diff --git a/toolkit/components/passwordmgr/test/test_maxforms_1.html b/toolkit/components/passwordmgr/test/test_maxforms_1.html index bcc2e29660cf..d7fa80c52f0a 100644 --- a/toolkit/components/passwordmgr/test/test_maxforms_1.html +++ b/toolkit/components/passwordmgr/test/test_maxforms_1.html @@ -52,12 +52,7 @@ function startTest() { SimpleTest.finish(); } -if (SpecialPowers.getBoolPref("signon.useDOMFormHasPassword")) { - info("skipping test when signon.useDOMFormHasPassword is enabled"); - SimpleTest.finish(); -} else { - window.onload = startTest; -} +window.onload = startTest; diff --git a/toolkit/components/passwordmgr/test/test_maxforms_2.html b/toolkit/components/passwordmgr/test/test_maxforms_2.html index da315c420294..281f12d17a1d 100644 --- a/toolkit/components/passwordmgr/test/test_maxforms_2.html +++ b/toolkit/components/passwordmgr/test/test_maxforms_2.html @@ -52,12 +52,7 @@ function startTest() { SimpleTest.finish(); } -if (SpecialPowers.getBoolPref("signon.useDOMFormHasPassword")) { - info("skipping test when signon.useDOMFormHasPassword is enabled"); - SimpleTest.finish(); -} else { - window.onload = startTest; -} +window.onload = startTest; diff --git a/toolkit/components/passwordmgr/test/test_maxforms_3.html b/toolkit/components/passwordmgr/test/test_maxforms_3.html index 63d2bfedd497..84fa863f42f3 100644 --- a/toolkit/components/passwordmgr/test/test_maxforms_3.html +++ b/toolkit/components/passwordmgr/test/test_maxforms_3.html @@ -52,12 +52,7 @@ function startTest() { SimpleTest.finish(); } -if (SpecialPowers.getBoolPref("signon.useDOMFormHasPassword")) { - info("skipping test when signon.useDOMFormHasPassword is enabled"); - SimpleTest.finish(); -} else { - window.onload = startTest; -} +window.onload = startTest; From ca6615e25a876cffbbfe7f8cfc9795262c69fd7d Mon Sep 17 00:00:00 2001 From: Gijs Kruitbosch Date: Thu, 22 Aug 2013 12:40:30 +0200 Subject: [PATCH 14/54] Bug 826603 - go button is too large on hidpi when stop/reload buttons aren't combined, r=dao --- browser/themes/osx/browser.css | 1 + 1 file changed, 1 insertion(+) diff --git a/browser/themes/osx/browser.css b/browser/themes/osx/browser.css index deb74e5c79aa..88a415697b07 100644 --- a/browser/themes/osx/browser.css +++ b/browser/themes/osx/browser.css @@ -1616,6 +1616,7 @@ window[tabsontop="false"] richlistitem[type~="action"][actiontype="switchtab"][s #go-button, #urlbar-go-button { -moz-image-region: rect(0, 84px, 28px, 56px); + height: 14px; } #go-button:hover:active, From 288e2d56d736ce29e96354c4dc81cfa5e2f03123 Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Thu, 22 Aug 2013 12:44:05 +0100 Subject: [PATCH 15/54] Bug 907624 - Change testTabHistory to inherit from BaseTest (r=gbrown) --- mobile/android/base/tests/testTabHistory.java.in | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mobile/android/base/tests/testTabHistory.java.in b/mobile/android/base/tests/testTabHistory.java.in index 62ed81c72c2f..20ef6526fd93 100644 --- a/mobile/android/base/tests/testTabHistory.java.in +++ b/mobile/android/base/tests/testTabHistory.java.in @@ -1,7 +1,7 @@ #filter substitution package @ANDROID_PACKAGE_NAME@.tests; -public class testTabHistory extends PixelTest { +public class testTabHistory extends BaseTest { @Override protected int getTestType() { @@ -21,11 +21,11 @@ public class testTabHistory extends PixelTest { String url3 = getAbsoluteUrl("/robocop/robocop_blank_03.html"); // Create tab history - loadAndPaint(url); + inputAndLoadUrl(url); verifyPageTitle("Browser Blank Page 01"); - loadAndPaint(url2); + inputAndLoadUrl(url2); verifyPageTitle("Browser Blank Page 02"); - loadAndPaint(url3); + inputAndLoadUrl(url3); verifyPageTitle("Browser Blank Page 03"); // Get the device information and create the navigation for it From ef566b4301c290057316e88b1e24326ce5127d8f Mon Sep 17 00:00:00 2001 From: Ekanan Ketunuti Date: Thu, 22 Aug 2013 09:57:42 -0400 Subject: [PATCH 16/54] Bug 907313 - Add "recurse", "recursed", "recurses", "recursing" and "recursive" to en-US dictionary. r=ehsan --- .../dictionary-sources/upstream-hunspell.diff | 80 ++++++++++--------- .../locales/en-US/hunspell/en-US.dic | 3 +- 2 files changed, 43 insertions(+), 40 deletions(-) diff --git a/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/upstream-hunspell.diff b/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/upstream-hunspell.diff index 1f4d78af08e7..bd3a04b0bbf2 100644 --- a/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/upstream-hunspell.diff +++ b/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/upstream-hunspell.diff @@ -9463,127 +9463,129 @@ > quinoa 40036a45854 > recency -40141a45960 +40140a45959 +> recurse/DGSV +40141a45961 > recuse/DGS -40208a46028 +40208a46029 > refactor/SMDG -40244d46063 +40244d46064 < reflexion/SM -40829c46648 +40829c46649 < reverie/M --- > reverie/MS -41415a47235 +41415a47236 > sabre/MS -41914c47734 +41914c47735 < schnaps's --- > schnaps/M -41949c47769 +41949c47770 < schrod's --- > schrod/SM -41998a47819 +41998a47820 > scot-free -42883,42885c48704 +42883,42885c48705 < shit's < shit/S! < shite/S! --- > shit/MS! -42887,42888c48706,48707 +42887,42888c48707,48708 < shithead/S! < shitload/! --- > shithead/MS! > shitload/MS! -42891c48710 +42891c48711 < shitty/RT! --- > shitty/TR! -42976a48796 +42976a48797 > should've -43008c48828 +43008c48829 < showtime --- > showtime/MS -43724,43726c49544 +43724,43726c49545 < smoulder's < smouldered < smoulders --- > smoulder/GSMD -44062c49880 +44062c49881 < sonofabitch --- > sonofabitch/! -44346a50165 +44346a50166 > spelled -44348a50168 +44348a50169 > spelt -44371a50192 +44371a50193 > spick/S! -44383c50204 +44383c50205 < spik/S --- > spik/S! -46106a51928 +46106a51929 > syllabi -46160c51982 +46160c51983 < synch/GMD --- > synch/GMDS -46167d51988 +46167d51989 < synchs -46203,46204c52024,52025 +46203,46204c52025,52026 < sysadmin/S < sysop/S --- > sysadmin/MS > sysop/MS -46752a52574 +46752a52575 > terabit/MS -46753a52576,52577 +46753a52577,52578 > terahertz/M > terapixel/MS -46817a52642 +46817a52643 > testcase/MS -46831a52657 +46831a52658 > testsuite/MS -46925a52752 +46925a52753 > theremin/MS -47755a53583 +47755a53584 > transfect/DSMG -47774a53603,53604 +47774a53604,53605 > transgenderism > transgene/MS -47951c53781 +47951c53782 < triage/M --- > triage/MG -48869a54700 +48869a54701 > unlikeable -49211c55042 +49211c55043 < vagina/M --- > vagina/MS -49368,49369c55199 +49368,49369c55200 < velour's < velours's --- > velour/MS -49478a55309 +49478a55310 > vertices -50148a55980 +50148a55981 > weaponize/DSG -50260,50261d56091 +50260,50261d56092 < werwolf/M < werwolves -50728c56558 +50728c56559 < women --- > women/M -50794c56624 +50794c56625 < wop/S! --- > wop/MS! diff --git a/extensions/spellcheck/locales/en-US/hunspell/en-US.dic b/extensions/spellcheck/locales/en-US/hunspell/en-US.dic index 8dc827ec5ece..de8590a463cd 100644 --- a/extensions/spellcheck/locales/en-US/hunspell/en-US.dic +++ b/extensions/spellcheck/locales/en-US/hunspell/en-US.dic @@ -1,4 +1,4 @@ -57446 +57447 0/nm 0th/pt 1/n1 @@ -46282,6 +46282,7 @@ recur/S recurred recurrence/SM recurring +recurse/DGSV recursion/S recuse/DGS recyclable/MS From ab71e7c01679c5bf8826893926c5508a80f05e13 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Thu, 22 Aug 2013 09:58:00 -0400 Subject: [PATCH 17/54] Bug 906506 - Properly commit onKeyMultiple() keys with unknown code. r=cpeterson --- mobile/android/base/GeckoInputConnection.java | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/mobile/android/base/GeckoInputConnection.java b/mobile/android/base/GeckoInputConnection.java index cc4d2c82242a..1b6724fd88b9 100644 --- a/mobile/android/base/GeckoInputConnection.java +++ b/mobile/android/base/GeckoInputConnection.java @@ -125,16 +125,22 @@ class GeckoInputConnection } } + public void runOnIcThread(final Handler uiHandler, + final GeckoEditableClient client, + final Runnable runnable) { + final Handler icHandler = client.getInputConnectionHandler(); + if (icHandler.getLooper() == uiHandler.getLooper()) { + // IC thread is UI thread; safe to run directly + runnable.run(); + return; + } + runOnIcThread(icHandler, runnable); + } + public void sendEventFromUiThread(final Handler uiHandler, final GeckoEditableClient client, final GeckoEvent event) { - final Handler icHandler = client.getInputConnectionHandler(); - if (icHandler.getLooper() == uiHandler.getLooper()) { - // IC thread is UI thread; safe to send event directly - client.sendEvent(event); - return; - } - runOnIcThread(icHandler, new Runnable() { + runOnIcThread(uiHandler, client, new Runnable() { @Override public void run() { client.sendEvent(event); } @@ -849,10 +855,22 @@ class GeckoInputConnection } @Override - public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) { + public boolean onKeyMultiple(int keyCode, int repeatCount, final KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_UNKNOWN) { // KEYCODE_UNKNOWN means the characters are in KeyEvent.getCharacters() - return commitText(event.getCharacters(), 1); + View view = getView(); + if (view != null) { + InputThreadUtils.sInstance.runOnIcThread( + view.getRootView().getHandler(), mEditableClient, + new Runnable() { + @Override public void run() { + // Don't call GeckoInputConnection.commitText because it can + // post a key event back to onKeyMultiple, causing a loop + GeckoInputConnection.super.commitText(event.getCharacters(), 1); + } + }); + } + return true; } while ((repeatCount--) != 0) { if (!processKey(keyCode, event, true) || From 1c4f38a7c3b8b8d9ac18e18217e2ea4aae53de44 Mon Sep 17 00:00:00 2001 From: Gavin Sharp Date: Wed, 21 Aug 2013 16:45:52 -0700 Subject: [PATCH 18/54] Bug 907430: XBL constructors/destructors should have names, r=bz --HG-- extra : transplant_source : G%E8%B6%A4%9D%E3%A2%F8C%81%1Dc%3E%FC%CD%13%5Ea%8A%E0 --- content/xbl/src/nsXBLContentSink.cpp | 25 ++++++++++++++++++------ content/xbl/src/nsXBLContentSink.h | 2 ++ content/xbl/src/nsXBLProtoImpl.cpp | 12 ++++++++++-- content/xbl/src/nsXBLProtoImplMethod.cpp | 3 +++ content/xbl/src/nsXBLProtoImplMethod.h | 4 ++-- content/xbl/src/nsXBLSerialize.h | 2 +- 6 files changed, 37 insertions(+), 11 deletions(-) diff --git a/content/xbl/src/nsXBLContentSink.cpp b/content/xbl/src/nsXBLContentSink.cpp index 10f0273f08e8..cda14c82e6d0 100644 --- a/content/xbl/src/nsXBLContentSink.cpp +++ b/content/xbl/src/nsXBLContentSink.cpp @@ -54,7 +54,7 @@ nsXBLContentSink::nsXBLContentSink() mSecondaryState(eXBL_None), mDocInfo(nullptr), mIsChromeOrResource(false), - mFoundFirstBinding(false), + mFoundFirstBinding(false), mBinding(nullptr), mHandler(nullptr), mImplementation(nullptr), @@ -453,8 +453,15 @@ nsXBLContentSink::OnOpenContainer(const PRUnichar **aAtts, NS_ASSERTION(mBinding, "Must have binding here"); mSecondaryState = eXBL_InConstructor; + nsAutoString name; + if (!mCurrentBindingID.IsEmpty()) { + name.Assign(mCurrentBindingID); + name.AppendLiteral("_XBL_Constructor"); + } else { + name.AppendLiteral("XBL_Constructor"); + } nsXBLProtoImplAnonymousMethod* newMethod = - new nsXBLProtoImplAnonymousMethod(); + new nsXBLProtoImplAnonymousMethod(name.get()); if (newMethod) { newMethod->SetLineNumber(aLineNumber); mBinding->SetConstructor(newMethod); @@ -466,8 +473,15 @@ nsXBLContentSink::OnOpenContainer(const PRUnichar **aAtts, mSecondaryState == eXBL_None); NS_ASSERTION(mBinding, "Must have binding here"); mSecondaryState = eXBL_InDestructor; + nsAutoString name; + if (!mCurrentBindingID.IsEmpty()) { + name.Assign(mCurrentBindingID); + name.AppendLiteral("_XBL_Destructor"); + } else { + name.AppendLiteral("XBL_Destructor"); + } nsXBLProtoImplAnonymousMethod* newMethod = - new nsXBLProtoImplAnonymousMethod(); + new nsXBLProtoImplAnonymousMethod(name.get()); if (newMethod) { newMethod->SetLineNumber(aLineNumber); mBinding->SetDestructor(newMethod); @@ -529,9 +543,8 @@ nsresult nsXBLContentSink::ConstructBinding(uint32_t aLineNumber) { nsCOMPtr binding = GetCurrentContent(); - nsAutoString id; - binding->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id); - NS_ConvertUTF16toUTF8 cid(id); + binding->GetAttr(kNameSpaceID_None, nsGkAtoms::id, mCurrentBindingID); + NS_ConvertUTF16toUTF8 cid(mCurrentBindingID); nsresult rv = NS_OK; diff --git a/content/xbl/src/nsXBLContentSink.h b/content/xbl/src/nsXBLContentSink.h index 75cd6dd851da..d0dd382aaca9 100644 --- a/content/xbl/src/nsXBLContentSink.h +++ b/content/xbl/src/nsXBLContentSink.h @@ -135,6 +135,8 @@ protected: bool mIsChromeOrResource; // For bug #45989 bool mFoundFirstBinding; + nsString mCurrentBindingID; + nsXBLPrototypeBinding* mBinding; nsXBLPrototypeHandler* mHandler; // current handler, owned by its PrototypeBinding nsXBLProtoImpl* mImplementation; diff --git a/content/xbl/src/nsXBLProtoImpl.cpp b/content/xbl/src/nsXBLProtoImpl.cpp index 6025464f404e..07cd1df16417 100644 --- a/content/xbl/src/nsXBLProtoImpl.cpp +++ b/content/xbl/src/nsXBLProtoImpl.cpp @@ -397,7 +397,11 @@ nsXBLProtoImpl::Read(nsIObjectInputStream* aStream, } case XBLBinding_Serialize_Constructor: { - mConstructor = new nsXBLProtoImplAnonymousMethod(); + nsAutoString name; + rv = aStream->ReadString(name); + NS_ENSURE_SUCCESS(rv, rv); + + mConstructor = new nsXBLProtoImplAnonymousMethod(name.get()); rv = mConstructor->Read(aStream); if (NS_FAILED(rv)) { delete mConstructor; @@ -410,7 +414,11 @@ nsXBLProtoImpl::Read(nsIObjectInputStream* aStream, } case XBLBinding_Serialize_Destructor: { - mDestructor = new nsXBLProtoImplAnonymousMethod(); + nsAutoString name; + rv = aStream->ReadString(name); + NS_ENSURE_SUCCESS(rv, rv); + + mDestructor = new nsXBLProtoImplAnonymousMethod(name.get()); rv = mDestructor->Read(aStream); if (NS_FAILED(rv)) { delete mDestructor; diff --git a/content/xbl/src/nsXBLProtoImplMethod.cpp b/content/xbl/src/nsXBLProtoImplMethod.cpp index 5c5ba1075217..69473738d706 100644 --- a/content/xbl/src/nsXBLProtoImplMethod.cpp +++ b/content/xbl/src/nsXBLProtoImplMethod.cpp @@ -372,6 +372,9 @@ nsXBLProtoImplAnonymousMethod::Write(nsIObjectOutputStream* aStream, nsresult rv = aStream->Write8(aType); NS_ENSURE_SUCCESS(rv, rv); + rv = aStream->WriteWStringZ(mName); + NS_ENSURE_SUCCESS(rv, rv); + // Calling fromMarkedLocation() is safe because mMethod is traced by the // Trace() method above, and because its value is never changed after it has // been set to a compiled method. diff --git a/content/xbl/src/nsXBLProtoImplMethod.h b/content/xbl/src/nsXBLProtoImplMethod.h index f5a2152d8bce..ec24b4b2df92 100644 --- a/content/xbl/src/nsXBLProtoImplMethod.h +++ b/content/xbl/src/nsXBLProtoImplMethod.h @@ -131,8 +131,8 @@ protected: class nsXBLProtoImplAnonymousMethod : public nsXBLProtoImplMethod { public: - nsXBLProtoImplAnonymousMethod() : - nsXBLProtoImplMethod(EmptyString().get()) + nsXBLProtoImplAnonymousMethod(const PRUnichar* aName) : + nsXBLProtoImplMethod(aName) {} nsresult Execute(nsIContent* aBoundElement); diff --git a/content/xbl/src/nsXBLSerialize.h b/content/xbl/src/nsXBLSerialize.h index 26e3e259e3f2..74d30a6abdc1 100644 --- a/content/xbl/src/nsXBLSerialize.h +++ b/content/xbl/src/nsXBLSerialize.h @@ -16,7 +16,7 @@ typedef uint8_t XBLBindingSerializeDetails; // A version number to ensure we don't load cached data in a different // file format. -#define XBLBinding_Serialize_Version 0x00000001 +#define XBLBinding_Serialize_Version 0x00000002 // Set for the first binding in a document #define XBLBinding_Serialize_IsFirstBinding 1 From ad8ecdedcbca9745f619eaf1eb051544b460ac46 Mon Sep 17 00:00:00 2001 From: Brandon Benvie Date: Mon, 19 Aug 2013 11:49:18 -0700 Subject: [PATCH 19/54] Bug 905854 - The browser console shortcut is broken from Scratchpad. r=robcee, a=akeybl --- browser/devtools/scratchpad/scratchpad.js | 2 +- browser/devtools/scratchpad/test/Makefile.in | 1 + .../browser_scratchpad_open_error_console.js | 37 +++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 browser/devtools/scratchpad/test/browser_scratchpad_open_error_console.js diff --git a/browser/devtools/scratchpad/scratchpad.js b/browser/devtools/scratchpad/scratchpad.js index 5f329025a5bd..ea01475dd24d 100644 --- a/browser/devtools/scratchpad/scratchpad.js +++ b/browser/devtools/scratchpad/scratchpad.js @@ -1175,7 +1175,7 @@ var Scratchpad = { */ openErrorConsole: function SP_openErrorConsole() { - this.browserWindow.HUDConsoleUI.toggleBrowserConsole(); + this.browserWindow.HUDService.toggleBrowserConsole(); }, /** diff --git a/browser/devtools/scratchpad/test/Makefile.in b/browser/devtools/scratchpad/test/Makefile.in index 570b79737dcf..e24f3d2363cd 100644 --- a/browser/devtools/scratchpad/test/Makefile.in +++ b/browser/devtools/scratchpad/test/Makefile.in @@ -38,6 +38,7 @@ MOCHITEST_BROWSER_FILES = \ browser_scratchpad_bug_644413_modeline.js \ browser_scratchpad_bug807924_cannot_convert_to_string.js \ browser_scratchpad_long_string.js \ + browser_scratchpad_open_error_console.js \ head.js \ # Disable test due to bug 807234 becoming basically permanent diff --git a/browser/devtools/scratchpad/test/browser_scratchpad_open_error_console.js b/browser/devtools/scratchpad/test/browser_scratchpad_open_error_console.js new file mode 100644 index 000000000000..2ac937ccbcb5 --- /dev/null +++ b/browser/devtools/scratchpad/test/browser_scratchpad_open_error_console.js @@ -0,0 +1,37 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function test() +{ + waitForExplicitFinish(); + + gBrowser.selectedTab = gBrowser.addTab(); + gBrowser.selectedBrowser.addEventListener("load", function onLoad() { + gBrowser.selectedBrowser.removeEventListener("load", onLoad, true); + openScratchpad(runTests); + }, true); + + content.location = "data:text/html;charset=utf8,test Scratchpad." + + "openErrorConsole()"; +} + +function runTests() +{ + Services.obs.addObserver(function observer(aSubject) { + Services.obs.removeObserver(observer, "web-console-created"); + aSubject.QueryInterface(Ci.nsISupportsString); + + let hud = HUDService.getBrowserConsole(); + ok(hud, "browser console is open"); + is(aSubject.data, hud.hudId, "notification hudId is correct"); + + HUDService.toggleBrowserConsole().then(finish); + }, "web-console-created", false); + + let hud = HUDService.getBrowserConsole(); + ok(!hud, "browser console is not open"); + info("wait for the browser console to open from Scratchpad"); + + gScratchpadWindow.Scratchpad.openErrorConsole(); +} From 9addcc70289332e9aeea0a44241c4c3dc3aae1d9 Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Thu, 22 Aug 2013 09:00:37 +0200 Subject: [PATCH 20/54] Backout changeset b43d38c5c456 for build bustage. --- js/xpconnect/src/Sandbox.cpp | 1572 ---------------------------- js/xpconnect/src/XPCComponents.cpp | 1545 ++++++++++++++++++++++++++- js/xpconnect/src/moz.build | 1 - js/xpconnect/src/xpcprivate.h | 15 - 4 files changed, 1539 insertions(+), 1594 deletions(-) delete mode 100644 js/xpconnect/src/Sandbox.cpp diff --git a/js/xpconnect/src/Sandbox.cpp b/js/xpconnect/src/Sandbox.cpp deleted file mode 100644 index c97b052ff2a3..000000000000 --- a/js/xpconnect/src/Sandbox.cpp +++ /dev/null @@ -1,1572 +0,0 @@ -/* -*- 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/. */ - -/* - * The Components.Sandbox object. - */ - -#include "AccessCheck.h" -#include "jsfriendapi.h" -#include "jsproxy.h" -#include "nsContentUtils.h" -#include "nsCxPusher.h" -#include "nsGlobalWindow.h" -#include "nsIDOMWindow.h" -#include "nsIScriptContext.h" -#include "nsIScriptObjectPrincipal.h" -#include "nsIScriptSecurityManager.h" -#include "nsIURI.h" -#include "nsJSEnvironment.h" -#include "nsJSUtils.h" -#include "nsNetUtil.h" -#include "nsNullPrincipal.h" -#include "nsPrincipal.h" -#include "nsXMLHttpRequest.h" -#include "WrapperFactory.h" -#include "XPCJSWeakReference.h" -#include "xpcprivate.h" -#include "XPCQuickStubs.h" -#include "XPCWrapper.h" -#include "XrayWrapper.h" -#include "mozilla/dom/BindingUtils.h" - -using namespace mozilla; -using namespace js; -using namespace xpc; - -using mozilla::dom::DestroyProtoAndIfaceCache; - -NS_IMPL_ISUPPORTS3(SandboxPrivate, - nsIScriptObjectPrincipal, - nsIGlobalObject, - nsISupportsWeakReference) - -const char kScriptSecurityManagerContractID[] = NS_SCRIPTSECURITYMANAGER_CONTRACTID; - -class nsXPCComponents_utils_Sandbox : public nsIXPCComponents_utils_Sandbox, - public nsIXPCScriptable -{ -public: - // Aren't macros nice? - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_NSIXPCCOMPONENTS_UTILS_SANDBOX - NS_DECL_NSIXPCSCRIPTABLE - -public: - nsXPCComponents_utils_Sandbox(); - virtual ~nsXPCComponents_utils_Sandbox(); - -private: - static nsresult CallOrConstruct(nsIXPConnectWrappedNative *wrapper, - JSContext *cx, HandleObject obj, - const CallArgs &args, bool *_retval); -}; - -already_AddRefed -NewSandboxConstructor() -{ - nsCOMPtr sbConstructor = - new nsXPCComponents_utils_Sandbox(); - return sbConstructor.forget(); -} - -static bool -SandboxDump(JSContext *cx, unsigned argc, jsval *vp) -{ - JSString *str; - if (!argc) - return true; - - str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]); - if (!str) - return false; - - size_t length; - const jschar *chars = JS_GetStringCharsZAndLength(cx, str, &length); - if (!chars) - return false; - - nsDependentString wstr(chars, length); - char *cstr = ToNewUTF8String(wstr); - if (!cstr) - return false; - -#if defined(XP_MACOSX) - // Be nice and convert all \r to \n. - char *c = cstr, *cEnd = cstr + strlen(cstr); - while (c < cEnd) { - if (*c == '\r') - *c = '\n'; - c++; - } -#endif - - fputs(cstr, stdout); - fflush(stdout); - NS_Free(cstr); - JS_SET_RVAL(cx, vp, JSVAL_TRUE); - return true; -} - -static bool -SandboxDebug(JSContext *cx, unsigned argc, jsval *vp) -{ -#ifdef DEBUG - return SandboxDump(cx, argc, vp); -#else - return true; -#endif -} - -static bool -SandboxImport(JSContext *cx, unsigned argc, Value *vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - if (args.length() < 1 || args[0].isPrimitive()) { - XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx); - return false; - } - - RootedString funname(cx); - if (args.length() > 1) { - // Use the second parameter as the function name. - funname = JS_ValueToString(cx, args[1]); - if (!funname) - return false; - } else { - // NB: funobj must only be used to get the JSFunction out. - RootedObject funobj(cx, &args[0].toObject()); - if (js::IsProxy(funobj)) { - funobj = XPCWrapper::UnsafeUnwrapSecurityWrapper(funobj); - } - - JSAutoCompartment ac(cx, funobj); - - JSFunction *fun = JS_ValueToFunction(cx, ObjectValue(*funobj)); - if (!fun) { - XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx); - return false; - } - - // Use the actual function name as the name. - funname = JS_GetFunctionId(fun); - if (!funname) { - XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx); - return false; - } - } - - RootedId id(cx); - if (!JS_ValueToId(cx, StringValue(funname), id.address())) - return false; - - // We need to resolve the this object, because this function is used - // unbound and should still work and act on the original sandbox. - RootedObject thisObject(cx, JS_THIS_OBJECT(cx, vp)); - if (!thisObject) { - XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx); - return false; - } - if (!JS_SetPropertyById(cx, thisObject, id, args[0])) - return false; - - args.rval().setUndefined(); - return true; -} - -static bool -CreateXMLHttpRequest(JSContext *cx, unsigned argc, jsval *vp) -{ - nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); - if (!ssm) - return false; - - nsIPrincipal *subjectPrincipal = ssm->GetCxSubjectPrincipal(cx); - if (!subjectPrincipal) - return false; - - RootedObject global(cx, JS::CurrentGlobalOrNull(cx)); - MOZ_ASSERT(global); - - nsIScriptObjectPrincipal *sop = - static_cast(xpc_GetJSPrivate(global)); - nsCOMPtr iglobal = do_QueryInterface(sop); - - nsCOMPtr xhr = new nsXMLHttpRequest(); - nsresult rv = xhr->Init(subjectPrincipal, nullptr, iglobal, nullptr); - if (NS_FAILED(rv)) - return false; - - rv = nsContentUtils::WrapNative(cx, global, xhr, vp); - if (NS_FAILED(rv)) - return false; - - return true; -} - -/* - * Instead of simply wrapping a function into another compartment, - * this helper function creates a native function in the target - * compartment and forwards the call to the original function. - * That call will be different than a regular JS function call in - * that, the |this| is left unbound, and all the non-native JS - * object arguments will be cloned using the structured clone - * algorithm. - * The return value is the new forwarder function, wrapped into - * the caller's compartment. - * The 3rd argument is the name of the property that will - * be set on the target scope, with the forwarder function as - * the value. - * The principal of the caller must subsume that of the target. - * - * Expected type of the arguments and the return value: - * function exportFunction(function funToExport, - * object targetScope, - * string name) - */ -static bool -ExportFunction(JSContext *cx, unsigned argc, jsval *vp) -{ - MOZ_ASSERT(cx); - if (argc < 3) { - JS_ReportError(cx, "Function requires at least 3 arguments"); - return false; - } - - CallArgs args = CallArgsFromVp(argc, vp); - if (!args[0].isObject() || !args[1].isObject() || !args[2].isString()) { - JS_ReportError(cx, "Invalid argument"); - return false; - } - - RootedObject funObj(cx, &args[0].toObject()); - RootedObject targetScope(cx, &args[1].toObject()); - RootedString funName(cx, args[2].toString()); - - // We can only export functions to scopes those are transparent for us, - // so if there is a security wrapper around targetScope we must throw. - targetScope = CheckedUnwrap(targetScope); - if (!targetScope) { - JS_ReportError(cx, "Permission denied to export function into scope"); - return false; - } - - if (JS_GetStringLength(funName) == 0) { - JS_ReportError(cx, "3rd argument should be a non-empty string"); - return false; - } - - { - // We need to operate in the target scope from here on, let's enter - // its compartment. - JSAutoCompartment ac(cx, targetScope); - - // Unwrapping to see if we have a callable. - funObj = UncheckedUnwrap(funObj); - if (!JS_ObjectIsCallable(cx, funObj)) { - JS_ReportError(cx, "First argument must be a function"); - return false; - } - - // The function forwarder will live in the target compartment. Since - // this function will be referenced from its private slot, to avoid a - // GC hazard, we must wrap it to the same compartment. - if (!JS_WrapObject(cx, funObj.address())) - return false; - - RootedId id(cx); - if (!JS_ValueToId(cx, args[2], id.address())) - return false; - - // And now, let's create the forwarder function in the target compartment - // for the function the be exported. - if (!NewFunctionForwarder(cx, id, funObj, /* doclone = */ true, args.rval())) { - JS_ReportError(cx, "Exporting function failed"); - return false; - } - - // We have the forwarder function in the target compartment, now - // we have to add it to the target scope as a property. - if (!JS_DefinePropertyById(cx, targetScope, id, args.rval(), - JS_PropertyStub, JS_StrictPropertyStub, - JSPROP_ENUMERATE)) - return false; - } - - // Finally we have to re-wrap the exported function back to the caller compartment. - if (!JS_WrapValue(cx, args.rval().address())) - return false; - - return true; -} - -static bool -GetFilenameAndLineNumber(JSContext *cx, nsACString &filename, unsigned &lineno) -{ - JSScript *script; - if (JS_DescribeScriptedCaller(cx, &script, &lineno)) { - if (const char *cfilename = JS_GetScriptFilename(cx, script)) { - filename.Assign(nsDependentCString(cfilename)); - return true; - } - } - return false; -} - -namespace xpc { -bool -IsReflector(JSObject *obj) -{ - return IS_WN_REFLECTOR(obj) || dom::IsDOMObject(obj); -} -} /* namespace xpc */ - -enum ForwarderCloneTags { - SCTAG_BASE = JS_SCTAG_USER_MIN, - SCTAG_REFLECTOR -}; - -static JSObject * -CloneNonReflectorsRead(JSContext *cx, JSStructuredCloneReader *reader, uint32_t tag, - uint32_t data, void *closure) -{ - MOZ_ASSERT(closure, "Null pointer!"); - AutoObjectVector *reflectors = static_cast(closure); - if (tag == SCTAG_REFLECTOR) { - MOZ_ASSERT(!data); - - size_t idx; - if (JS_ReadBytes(reader, &idx, sizeof(size_t))) { - RootedObject reflector(cx, reflectors->handleAt(idx)); - MOZ_ASSERT(reflector, "No object pointer?"); - MOZ_ASSERT(IsReflector(reflector), "Object pointer must be a reflector!"); - - JS_WrapObject(cx, reflector.address()); - JS_ASSERT(WrapperFactory::IsXrayWrapper(reflector) || - IsReflector(reflector)); - - return reflector; - } - } - - JS_ReportError(cx, "CloneNonReflectorsRead error"); - return nullptr; -} - -static bool -CloneNonReflectorsWrite(JSContext *cx, JSStructuredCloneWriter *writer, - Handle obj, void *closure) -{ - MOZ_ASSERT(closure, "Null pointer!"); - - // We need to maintain a list of reflectors to make sure all these objects - // are properly rooter. Only their indices will be serialized. - AutoObjectVector *reflectors = static_cast(closure); - if (IsReflector(obj)) { - if (!reflectors->append(obj)) - return false; - - size_t idx = reflectors->length()-1; - if (JS_WriteUint32Pair(writer, SCTAG_REFLECTOR, 0) && - JS_WriteBytes(writer, &idx, sizeof(size_t))) { - return true; - } - } - - JS_ReportError(cx, "CloneNonReflectorsWrite error"); - return false; -} - -JSStructuredCloneCallbacks gForwarderStructuredCloneCallbacks = { - CloneNonReflectorsRead, - CloneNonReflectorsWrite, - nullptr -}; - -/* - * This is a special structured cloning, that clones only non-reflectors. - * The function assumes the cx is already entered the compartment we want - * to clone to, and that if val is an object is from the compartment we - * clone from. - */ -bool -CloneNonReflectors(JSContext *cx, MutableHandleValue val) -{ - JSAutoStructuredCloneBuffer buffer; - AutoObjectVector rootedReflectors(cx); - { - // For parsing val we have to enter its compartment. - // (unless it's a primitive) - Maybe ac; - if (val.isObject()) { - ac.construct(cx, &val.toObject()); - } - - if (!buffer.write(cx, val, - &gForwarderStructuredCloneCallbacks, - &rootedReflectors)) - { - return false; - } - } - - // Now recreate the clones in the target compartment. - RootedValue rval(cx); - if (!buffer.read(cx, val.address(), - &gForwarderStructuredCloneCallbacks, - &rootedReflectors)) - { - return false; - } - - return true; -} - -/* - * Similar to evalInSandbox except this one is used to eval a script in the - * scope of a window. Also note, that the return value and the possible exceptions - * in the script are structured cloned, unless they are natives (then they are just - * wrapped). - * Principal of the caller must subsume the target's. - * - * Expected type of the arguments: - * value evalInWindow(string script, - * object window) - */ -static bool -EvalInWindow(JSContext *cx, unsigned argc, jsval *vp) -{ - MOZ_ASSERT(cx); - if (argc < 2) { - JS_ReportError(cx, "Function requires two arguments"); - return false; - } - - CallArgs args = CallArgsFromVp(argc, vp); - if (!args[0].isString() || !args[1].isObject()) { - JS_ReportError(cx, "Invalid arguments"); - return false; - } - - RootedString srcString(cx, args[0].toString()); - RootedObject targetScope(cx, &args[1].toObject()); - - // If we cannot unwrap we must not eval in it. - targetScope = CheckedUnwrap(targetScope); - if (!targetScope) { - JS_ReportError(cx, "Permission denied to eval in target scope"); - return false; - } - - // Make sure that we have a window object. - RootedObject inner(cx, CheckedUnwrap(targetScope, /* stopAtOuter = */ false)); - nsCOMPtr global; - nsCOMPtr window; - if (!JS_IsGlobalObject(inner) || - !(global = GetNativeForGlobal(inner)) || - !(window = do_QueryInterface(global))) - { - JS_ReportError(cx, "Second argument must be a window"); - return false; - } - - nsCOMPtr context = - (static_cast(window.get()))->GetScriptContext(); - if (!context) { - JS_ReportError(cx, "Script context needed"); - return false; - } - - if (!context->GetScriptsEnabled()) { - JS_ReportError(cx, "Scripts are disabled in this window"); - return false; - } - - nsCString filename; - unsigned lineNo; - if (!GetFilenameAndLineNumber(cx, filename, lineNo)) { - // Default values for non-scripted callers. - filename.Assign("Unknown"); - lineNo = 0; - } - - nsDependentJSString srcDepString; - srcDepString.init(cx, srcString); - - { - // CompileOptions must be created from the context - // we will execute this script in. - JSContext *wndCx = context->GetNativeContext(); - AutoCxPusher pusher(wndCx); - JS::CompileOptions compileOptions(wndCx); - compileOptions.setFileAndLine(filename.get(), lineNo); - - // We don't want the JS engine to automatically report - // uncaught exceptions. - nsJSUtils::EvaluateOptions evaluateOptions; - evaluateOptions.setReportUncaught(false); - - nsresult rv = nsJSUtils::EvaluateString(wndCx, - srcDepString, - targetScope, - compileOptions, - evaluateOptions, - args.rval().address()); - - if (NS_FAILED(rv)) { - // If there was an exception we get it as a return value, if - // the evaluation failed for some other reason, then a default - // exception is raised. - MOZ_ASSERT(!JS_IsExceptionPending(wndCx), - "Exception should be delivered as return value."); - if (args.rval().isUndefined()) { - MOZ_ASSERT(rv == NS_ERROR_OUT_OF_MEMORY); - return false; - } - - // If there was an exception thrown we should set it - // on the calling context. - RootedValue exn(wndCx, args.rval()); - // First we should reset the return value. - args.rval().set(UndefinedValue()); - - // Then clone the exception. - if (CloneNonReflectors(cx, &exn)) - JS_SetPendingException(cx, exn); - - return false; - } - } - - // Let's clone the return value back to the callers compartment. - if (!CloneNonReflectors(cx, args.rval())) { - args.rval().set(UndefinedValue()); - return false; - } - - return true; -} - -static bool -sandbox_enumerate(JSContext *cx, HandleObject obj) -{ - return JS_EnumerateStandardClasses(cx, obj); -} - -static bool -sandbox_resolve(JSContext *cx, HandleObject obj, HandleId id) -{ - bool resolved; - return JS_ResolveStandardClass(cx, obj, id, &resolved); -} - -static void -sandbox_finalize(JSFreeOp *fop, JSObject *obj) -{ - nsIScriptObjectPrincipal *sop = - static_cast(xpc_GetJSPrivate(obj)); - MOZ_ASSERT(sop); - static_cast(sop)->ForgetGlobalObject(); - NS_IF_RELEASE(sop); - DestroyProtoAndIfaceCache(obj); -} - -static bool -sandbox_convert(JSContext *cx, HandleObject obj, JSType type, MutableHandleValue vp) -{ - if (type == JSTYPE_OBJECT) { - vp.set(OBJECT_TO_JSVAL(obj)); - return true; - } - - return JS_ConvertStub(cx, obj, type, vp); -} - -static JSClass SandboxClass = { - "Sandbox", - XPCONNECT_GLOBAL_FLAGS, - JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, - sandbox_enumerate, sandbox_resolve, sandbox_convert, sandbox_finalize, - NULL, NULL, NULL, NULL, TraceXPCGlobal -}; - -static const JSFunctionSpec SandboxFunctions[] = { - JS_FS("dump", SandboxDump, 1,0), - JS_FS("debug", SandboxDebug, 1,0), - JS_FS("importFunction", SandboxImport, 1,0), - JS_FS_END -}; - -bool -IsSandbox(JSObject *obj) -{ - return GetObjectJSClass(obj) == &SandboxClass; -} - -/***************************************************************************/ -nsXPCComponents_utils_Sandbox::nsXPCComponents_utils_Sandbox() -{ -} - -nsXPCComponents_utils_Sandbox::~nsXPCComponents_utils_Sandbox() -{ -} - -NS_INTERFACE_MAP_BEGIN(nsXPCComponents_utils_Sandbox) - NS_INTERFACE_MAP_ENTRY(nsIXPCComponents_utils_Sandbox) - NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable) - NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_utils_Sandbox) -NS_INTERFACE_MAP_END_THREADSAFE - -NS_IMPL_ADDREF(nsXPCComponents_utils_Sandbox) -NS_IMPL_RELEASE(nsXPCComponents_utils_Sandbox) - -// We use the nsIXPScriptable macros to generate lots of stuff for us. -#define XPC_MAP_CLASSNAME nsXPCComponents_utils_Sandbox -#define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_utils_Sandbox" -#define XPC_MAP_WANT_CALL -#define XPC_MAP_WANT_CONSTRUCT -#define XPC_MAP_FLAGS 0 -#include "xpc_map_end.h" /* This #undef's the above. */ - -xpc::SandboxProxyHandler xpc::sandboxProxyHandler; - -bool -xpc::IsSandboxPrototypeProxy(JSObject *obj) -{ - return js::IsProxy(obj) && - js::GetProxyHandler(obj) == &xpc::sandboxProxyHandler; -} - -bool -xpc::SandboxCallableProxyHandler::call(JSContext *cx, JS::Handle proxy, - const JS::CallArgs &args) -{ - // We forward the call to our underlying callable. - - // The parent of our proxy is the SandboxProxyHandler proxy - RootedObject sandboxProxy(cx, JS_GetParent(proxy)); - MOZ_ASSERT(js::IsProxy(sandboxProxy) && - js::GetProxyHandler(sandboxProxy) == &xpc::sandboxProxyHandler); - - // The parent of the sandboxProxy is the sandbox global, and the - // target object is the original proto. - RootedObject sandboxGlobal(cx, JS_GetParent(sandboxProxy)); - MOZ_ASSERT(js::GetObjectJSClass(sandboxGlobal) == &SandboxClass); - - // If our this object is the sandbox global, we call with this set to the - // original proto instead. - // - // There are two different ways we can compute |this|. If we use - // JS_THIS_VALUE, we'll get the bonafide |this| value as passed by the - // caller, which may be undefined if a global function was invoked without - // an explicit invocant. If we use JS_THIS or JS_THIS_OBJECT, the |this| - // in |vp| will be coerced to the global, which is not the correct - // behavior in ES5 strict mode. And we have no way to compute strictness - // here. - // - // The naive approach is simply to use JS_THIS_VALUE here. If |this| was - // explicit, we can remap it appropriately. If it was implicit, then we - // leave it as undefined, and let the callee sort it out. Since the callee - // is generally in the same compartment as its global (eg the Window's - // compartment, not the Sandbox's), the callee will generally compute the - // correct |this|. - // - // However, this breaks down in the Xray case. If the sandboxPrototype - // is an Xray wrapper, then we'll end up reifying the native methods in - // the Sandbox's scope, which means that they'll compute |this| to be the - // Sandbox, breaking old-style XPC_WN_CallMethod methods. - // - // Luckily, the intent of Xrays is to provide a vanilla view of a foreign - // DOM interface, which means that we don't care about script-enacted - // strictness in the prototype's home compartment. Indeed, since DOM - // methods are always non-strict, we can just assume non-strict semantics - // if the sandboxPrototype is an Xray Wrapper, which lets us appropriately - // remap |this|. - JS::Value thisVal = - WrapperFactory::IsXrayWrapper(sandboxProxy) ? args.computeThis(cx) : args.thisv(); - if (thisVal == ObjectValue(*sandboxGlobal)) { - thisVal = ObjectValue(*js::GetProxyTargetObject(sandboxProxy)); - } - - return JS::Call(cx, thisVal, js::GetProxyPrivate(proxy), args.length(), args.array(), - args.rval()); -} - -xpc::SandboxCallableProxyHandler xpc::sandboxCallableProxyHandler; - -// Wrap a callable such that if we're called with oldThisObj as the -// "this" we will instead call it with newThisObj as the this. -static JSObject* -WrapCallable(JSContext *cx, JSObject *callable, JSObject *sandboxProtoProxy) -{ - MOZ_ASSERT(JS_ObjectIsCallable(cx, callable)); - // Our proxy is wrapping the callable. So we need to use the - // callable as the private. We use the given sandboxProtoProxy as - // the parent, and our call() hook depends on that. - MOZ_ASSERT(js::IsProxy(sandboxProtoProxy) && - js::GetProxyHandler(sandboxProtoProxy) == - &xpc::sandboxProxyHandler); - - RootedValue priv(cx, ObjectValue(*callable)); - return js::NewProxyObject(cx, &xpc::sandboxCallableProxyHandler, - priv, nullptr, - sandboxProtoProxy, js::ProxyIsCallable); -} - -template -bool BindPropertyOp(JSContext *cx, Op &op, JSPropertyDescriptor *desc, HandleId id, - unsigned attrFlag, HandleObject sandboxProtoProxy) -{ - if (!op) { - return true; - } - - RootedObject func(cx); - if (desc->attrs & attrFlag) { - // Already an object - func = JS_FUNC_TO_DATA_PTR(JSObject *, op); - } else { - // We have an actual property op. For getters, we use 0 - // args, for setters we use 1 arg. - uint32_t args = (attrFlag == JSPROP_GETTER) ? 0 : 1; - RootedObject obj(cx, desc->obj); - func = GeneratePropertyOp(cx, obj, id, args, op); - if (!func) - return false; - } - func = WrapCallable(cx, func, sandboxProtoProxy); - if (!func) - return false; - op = JS_DATA_TO_FUNC_PTR(Op, func.get()); - desc->attrs |= attrFlag; - return true; -} - -extern bool -XPC_WN_Helper_GetProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp); -extern bool -XPC_WN_Helper_SetProperty(JSContext *cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp); - -bool -xpc::SandboxProxyHandler::getPropertyDescriptor(JSContext *cx, - JS::Handle proxy, - JS::Handle id, - JS::MutableHandle desc, - unsigned flags) -{ - JS::RootedObject obj(cx, wrappedObject(proxy)); - - MOZ_ASSERT(js::GetObjectCompartment(obj) == js::GetObjectCompartment(proxy)); - if (!JS_GetPropertyDescriptorById(cx, obj, id, - flags, desc)) - return false; - - if (!desc.object()) - return true; // No property, nothing to do - - // Now fix up the getter/setter/value as needed to be bound to desc->obj - // Don't mess with holder_get and holder_set, though, because those rely on - // the "vp is prefilled with the value in the slot" behavior that property - // ops can in theory rely on, but our property op forwarder doesn't know how - // to make that happen. Since we really only need to rebind the DOM methods - // here, not rebindings holder_get and holder_set is OK. - // - // Similarly, don't mess with XPC_WN_Helper_GetProperty and - // XPC_WN_Helper_SetProperty, for the same reasons: that could confuse our - // access to expandos when we're not doing Xrays. - if (desc.getter() != xpc::holder_get && - desc.getter() != XPC_WN_Helper_GetProperty && - !BindPropertyOp(cx, desc.getter(), desc.address(), id, JSPROP_GETTER, proxy)) - return false; - if (desc.setter() != xpc::holder_set && - desc.setter() != XPC_WN_Helper_SetProperty && - !BindPropertyOp(cx, desc.setter(), desc.address(), id, JSPROP_SETTER, proxy)) - return false; - if (desc.value().isObject()) { - JSObject* val = &desc.value().toObject(); - if (JS_ObjectIsCallable(cx, val)) { - val = WrapCallable(cx, val, proxy); - if (!val) - return false; - desc.value().setObject(*val); - } - } - - return true; -} - -bool -xpc::SandboxProxyHandler::getOwnPropertyDescriptor(JSContext *cx, - JS::Handle proxy, - JS::Handle id, - JS::MutableHandle desc, - unsigned flags) -{ - if (!getPropertyDescriptor(cx, proxy, id, desc, flags)) - return false; - - if (desc.object() != wrappedObject(proxy)) - desc.object().set(nullptr); - - return true; -} - -/* - * Reuse the BaseProxyHandler versions of the derived traps that are implemented - * in terms of the fundamental traps. - */ - -bool -xpc::SandboxProxyHandler::has(JSContext *cx, JS::Handle proxy, - JS::Handle id, bool *bp) -{ - return BaseProxyHandler::has(cx, proxy, id, bp); -} -bool -xpc::SandboxProxyHandler::hasOwn(JSContext *cx, JS::Handle proxy, - JS::Handle id, bool *bp) -{ - return BaseProxyHandler::hasOwn(cx, proxy, id, bp); -} - -bool -xpc::SandboxProxyHandler::get(JSContext *cx, JS::Handle proxy, - JS::Handle receiver, - JS::Handle id, - JS::MutableHandle vp) -{ - return BaseProxyHandler::get(cx, proxy, receiver, id, vp); -} - -bool -xpc::SandboxProxyHandler::set(JSContext *cx, JS::Handle proxy, - JS::Handle receiver, - JS::Handle id, - bool strict, - JS::MutableHandle vp) -{ - return BaseProxyHandler::set(cx, proxy, receiver, id, strict, vp); -} - -bool -xpc::SandboxProxyHandler::keys(JSContext *cx, JS::Handle proxy, - AutoIdVector &props) -{ - return BaseProxyHandler::keys(cx, proxy, props); -} - -bool -xpc::SandboxProxyHandler::iterate(JSContext *cx, JS::Handle proxy, - unsigned flags, JS::MutableHandle vp) -{ - return BaseProxyHandler::iterate(cx, proxy, flags, vp); -} - -nsresult -xpc_CreateSandboxObject(JSContext *cx, jsval *vp, nsISupports *prinOrSop, SandboxOptions& options) -{ - // Create the sandbox global object - nsresult rv; - nsCOMPtr xpc(do_GetService(nsIXPConnect::GetCID(), &rv)); - if (NS_FAILED(rv)) - return NS_ERROR_XPC_UNEXPECTED; - - nsCOMPtr principal = do_QueryInterface(prinOrSop); - if (!principal) { - nsCOMPtr sop = do_QueryInterface(prinOrSop); - if (sop) { - principal = sop->GetPrincipal(); - } else { - principal = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv); - MOZ_ASSERT(NS_FAILED(rv) || principal, "Bad return from do_CreateInstance"); - - if (!principal || NS_FAILED(rv)) { - if (NS_SUCCEEDED(rv)) { - rv = NS_ERROR_FAILURE; - } - - return rv; - } - } - MOZ_ASSERT(principal); - } - - JS::CompartmentOptions compartmentOptions; - compartmentOptions.setZone(options.sameZoneAs - ? JS::SameZoneAs(js::UncheckedUnwrap(options.sameZoneAs)) - : JS::SystemZone); - RootedObject sandbox(cx, xpc::CreateGlobalObject(cx, &SandboxClass, - principal, compartmentOptions)); - if (!sandbox) - return NS_ERROR_FAILURE; - - // Set up the wantXrays flag, which indicates whether xrays are desired even - // for same-origin access. - // - // This flag has historically been ignored for chrome sandboxes due to - // quirks in the wrapping implementation that have now been removed. Indeed, - // same-origin Xrays for chrome->chrome access seems a bit superfluous. - // Arguably we should just flip the default for chrome and still honor the - // flag, but such a change would break code in subtle ways for minimal - // benefit. So we just switch it off here. - xpc::GetCompartmentPrivate(sandbox)->wantXrays = - AccessCheck::isChrome(sandbox) ? false : options.wantXrays; - - { - JSAutoCompartment ac(cx, sandbox); - - if (options.proto) { - bool ok = JS_WrapObject(cx, options.proto.address()); - if (!ok) - return NS_ERROR_XPC_UNEXPECTED; - - if (xpc::WrapperFactory::IsXrayWrapper(options.proto) && !options.wantXrays) { - RootedValue v(cx, ObjectValue(*options.proto)); - if (!xpc::WrapperFactory::WaiveXrayAndWrap(cx, v.address())) - return NS_ERROR_FAILURE; - options.proto = &v.toObject(); - } - - // Now check what sort of thing we've got in |proto| - JSObject *unwrappedProto = js::UncheckedUnwrap(options.proto, false); - js::Class *unwrappedClass = js::GetObjectClass(unwrappedProto); - if (IS_WN_CLASS(unwrappedClass) || - mozilla::dom::IsDOMClass(Jsvalify(unwrappedClass))) { - // Wrap it up in a proxy that will do the right thing in terms - // of this-binding for methods. - RootedValue priv(cx, ObjectValue(*options.proto)); - options.proto = js::NewProxyObject(cx, &xpc::sandboxProxyHandler, - priv, nullptr, sandbox); - if (!options.proto) - return NS_ERROR_OUT_OF_MEMORY; - } - - ok = JS_SetPrototype(cx, sandbox, options.proto); - if (!ok) - return NS_ERROR_XPC_UNEXPECTED; - } - - nsCOMPtr sbp = - new SandboxPrivate(principal, sandbox); - - // Pass on ownership of sbp to |sandbox|. - JS_SetPrivate(sandbox, sbp.forget().get()); - - bool allowComponents = nsContentUtils::IsSystemPrincipal(principal) || - nsContentUtils::IsExpandedPrincipal(principal); - if (options.wantComponents && allowComponents && - !nsXPCComponents::AttachComponentsObject(cx, GetObjectScope(sandbox))) - return NS_ERROR_XPC_UNEXPECTED; - - if (!XPCNativeWrapper::AttachNewConstructorObject(cx, sandbox)) - return NS_ERROR_XPC_UNEXPECTED; - - if (!JS_DefineFunctions(cx, sandbox, SandboxFunctions)) - return NS_ERROR_XPC_UNEXPECTED; - - if (options.wantXHRConstructor && - !JS_DefineFunction(cx, sandbox, "XMLHttpRequest", CreateXMLHttpRequest, 0, JSFUN_CONSTRUCTOR)) - return NS_ERROR_XPC_UNEXPECTED; - - if (options.wantExportHelpers && - (!JS_DefineFunction(cx, sandbox, "exportFunction", ExportFunction, 3, 0) || - !JS_DefineFunction(cx, sandbox, "evalInWindow", EvalInWindow, 2, 0))) - return NS_ERROR_XPC_UNEXPECTED; - - } - - if (vp) { - // We have this crazy behavior where wantXrays=false also implies that the - // returned sandbox is implicitly waived. We've stopped advertising it, but - // keep supporting it for now. - *vp = OBJECT_TO_JSVAL(sandbox); - if (options.wantXrays && !JS_WrapValue(cx, vp)) - return NS_ERROR_UNEXPECTED; - if (!options.wantXrays && !xpc::WrapperFactory::WaiveXrayAndWrap(cx, vp)) - return NS_ERROR_UNEXPECTED; - } - - // Set the location information for the new global, so that tools like - // about:memory may use that information - xpc::SetLocationForGlobal(sandbox, options.sandboxName); - - JS_FireOnNewGlobalObject(cx, sandbox); - - return NS_OK; -} - -/* bool call(in nsIXPConnectWrappedNative wrapper, - in JSContextPtr cx, - in JSObjectPtr obj, - in uint32_t argc, - in JSValPtr argv, - in JSValPtr vp); -*/ -NS_IMETHODIMP -nsXPCComponents_utils_Sandbox::Call(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *objArg, const CallArgs &args, bool *_retval) -{ - RootedObject obj(cx, objArg); - return CallOrConstruct(wrapper, cx, obj, args, _retval); -} - -/* bool construct(in nsIXPConnectWrappedNative wrapper, - in JSContextPtr cx, - in JSObjectPtr obj, - in uint32_t argc, - in JSValPtr argv, - in JSValPtr vp); -*/ -NS_IMETHODIMP -nsXPCComponents_utils_Sandbox::Construct(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *objArg, const CallArgs &args, bool *_retval) -{ - RootedObject obj(cx, objArg); - return CallOrConstruct(wrapper, cx, obj, args, _retval); -} - -// for sandbox constructor the first argument can be a URI string in which case -// we use the related Codebase Principal for the sandbox -nsresult -GetPrincipalFromString(JSContext *cx, HandleString codebase, nsIPrincipal **principal) -{ - MOZ_ASSERT(principal); - MOZ_ASSERT(codebase); - nsCOMPtr uri; - nsDependentJSString codebaseStr; - NS_ENSURE_TRUE(codebaseStr.init(cx, codebase), NS_ERROR_FAILURE); - nsresult rv = NS_NewURI(getter_AddRefs(uri), codebaseStr); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr secman = - do_GetService(kScriptSecurityManagerContractID); - NS_ENSURE_TRUE(secman, NS_ERROR_FAILURE); - - // We could allow passing in the app-id and browser-element info to the - // sandbox constructor. But creating a sandbox based on a string is a - // deprecated API so no need to add features to it. - rv = secman->GetNoAppCodebasePrincipal(uri, principal); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(*principal, NS_ERROR_FAILURE); - - return NS_OK; -} - -// for sandbox constructor the first argument can be a principal object or -// a script object principal (Document, Window) -nsresult -GetPrincipalOrSOP(JSContext *cx, HandleObject from, nsISupports **out) -{ - MOZ_ASSERT(out); - *out = NULL; - - nsXPConnect* xpc = nsXPConnect::XPConnect(); - nsCOMPtr wrapper; - xpc->GetWrappedNativeOfJSObject(cx, from, - getter_AddRefs(wrapper)); - - NS_ENSURE_TRUE(wrapper, NS_ERROR_INVALID_ARG); - - if (nsCOMPtr sop = do_QueryWrappedNative(wrapper)) { - sop.forget(out); - return NS_OK; - } - - nsCOMPtr principal = do_QueryWrappedNative(wrapper); - principal.forget(out); - NS_ENSURE_TRUE(*out, NS_ERROR_INVALID_ARG); - - return NS_OK; -} - -// the first parameter of the sandbox constructor might be an array of principals, either in string -// format or actual objects (see GetPrincipalOrSOP) -nsresult -GetExpandedPrincipal(JSContext *cx, HandleObject arrayObj, nsIExpandedPrincipal **out) -{ - MOZ_ASSERT(out); - uint32_t length; - - if (!JS_IsArrayObject(cx, arrayObj) || - !JS_GetArrayLength(cx, arrayObj, &length) || - !length) - { - // we need a white list of principals or uri strings to create an - // expanded principal, if we got an empty array or something else - // report error - return NS_ERROR_INVALID_ARG; - } - - nsTArray< nsCOMPtr > allowedDomains(length); - allowedDomains.SetLength(length); - nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); - NS_ENSURE_TRUE(ssm, NS_ERROR_XPC_UNEXPECTED); - - for (uint32_t i = 0; i < length; ++i) { - RootedValue allowed(cx); - if (!JS_GetElement(cx, arrayObj, i, &allowed)) - return NS_ERROR_INVALID_ARG; - - nsresult rv; - nsCOMPtr principal; - if (allowed.isString()) { - // in case of string let's try to fetch a codebase principal from it - RootedString str(cx, allowed.toString()); - rv = GetPrincipalFromString(cx, str, getter_AddRefs(principal)); - NS_ENSURE_SUCCESS(rv, rv); - } else if (allowed.isObject()) { - // in case of object let's see if it's a Principal or a ScriptObjectPrincipal - nsCOMPtr prinOrSop; - RootedObject obj(cx, &allowed.toObject()); - rv = GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr sop(do_QueryInterface(prinOrSop)); - principal = do_QueryInterface(prinOrSop); - if (sop) { - principal = sop->GetPrincipal(); - } - } - NS_ENSURE_TRUE(principal, NS_ERROR_INVALID_ARG); - - // We do not allow ExpandedPrincipals to contain any system principals - bool isSystem; - rv = ssm->IsSystemPrincipal(principal, &isSystem); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_FALSE(isSystem, NS_ERROR_INVALID_ARG); - allowedDomains[i] = principal; - } - - nsCOMPtr result = new nsExpandedPrincipal(allowedDomains); - result.forget(out); - return NS_OK; -} - -// helper that tries to get a property form the options object -nsresult -GetPropFromOptions(JSContext *cx, HandleObject from, const char *name, MutableHandleValue prop, - bool *found) -{ - if (!JS_HasProperty(cx, from, name, found)) - return NS_ERROR_INVALID_ARG; - - if (found && !JS_GetProperty(cx, from, name, prop)) - return NS_ERROR_INVALID_ARG; - - return NS_OK; -} - -// helper that tries to get a boolean property form the options object -nsresult -GetBoolPropFromOptions(JSContext *cx, HandleObject from, const char *name, bool *prop) -{ - MOZ_ASSERT(prop); - - - RootedValue value(cx); - bool found; - if (NS_FAILED(GetPropFromOptions(cx, from, name, &value, &found))) - return NS_ERROR_INVALID_ARG; - - if (!found) - return NS_OK; - - if (!value.isBoolean()) - return NS_ERROR_INVALID_ARG; - - *prop = value.toBoolean(); - return NS_OK; -} - -// helper that tries to get an object property form the options object -nsresult -GetObjPropFromOptions(JSContext *cx, HandleObject from, const char *name, JSObject **prop) -{ - MOZ_ASSERT(prop); - - RootedValue value(cx); - bool found; - if (NS_FAILED(GetPropFromOptions(cx, from, name, &value, &found))) - return NS_ERROR_INVALID_ARG; - - if (!found) { - *prop = NULL; - return NS_OK; - } - - if (!value.isObject()) - return NS_ERROR_INVALID_ARG; - - *prop = &value.toObject(); - return NS_OK; -} - -// helper that tries to get a string property form the options object -nsresult -GetStringPropFromOptions(JSContext *cx, HandleObject from, const char *name, nsCString &prop) -{ - RootedValue value(cx); - bool found; - nsresult rv = GetPropFromOptions(cx, from, name, &value, &found); - NS_ENSURE_SUCCESS(rv, rv); - - if (!found) - return NS_OK; - - NS_ENSURE_TRUE(value.isString(), NS_ERROR_INVALID_ARG); - - char *tmp = JS_EncodeString(cx, value.toString()); - NS_ENSURE_TRUE(tmp, NS_ERROR_INVALID_ARG); - prop.Adopt(tmp, strlen(tmp)); - return NS_OK; -} - -// helper that parsing the sandbox options object (from) and sets the fields of the incoming options struct (options) -nsresult -ParseOptionsObject(JSContext *cx, jsval from, SandboxOptions &options) -{ - NS_ENSURE_TRUE(from.isObject(), NS_ERROR_INVALID_ARG); - RootedObject optionsObject(cx, &from.toObject()); - nsresult rv = GetObjPropFromOptions(cx, optionsObject, - "sandboxPrototype", options.proto.address()); - NS_ENSURE_SUCCESS(rv, rv); - - rv = GetBoolPropFromOptions(cx, optionsObject, - "wantXrays", &options.wantXrays); - NS_ENSURE_SUCCESS(rv, rv); - - rv = GetBoolPropFromOptions(cx, optionsObject, - "wantComponents", &options.wantComponents); - NS_ENSURE_SUCCESS(rv, rv); - - rv = GetBoolPropFromOptions(cx, optionsObject, - "wantXHRConstructor", &options.wantXHRConstructor); - NS_ENSURE_SUCCESS(rv, rv); - - rv = GetBoolPropFromOptions(cx, optionsObject, - "wantExportHelpers", &options.wantExportHelpers); - NS_ENSURE_SUCCESS(rv, rv); - - rv = GetStringPropFromOptions(cx, optionsObject, - "sandboxName", options.sandboxName); - NS_ENSURE_SUCCESS(rv, rv); - - rv = GetObjPropFromOptions(cx, optionsObject, - "sameZoneAs", options.sameZoneAs.address()); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -static nsresult -AssembleSandboxMemoryReporterName(JSContext *cx, nsCString &sandboxName) -{ - // Use a default name when the caller did not provide a sandboxName. - if (sandboxName.IsEmpty()) - sandboxName = NS_LITERAL_CSTRING("[anonymous sandbox]"); - - nsXPConnect* xpc = nsXPConnect::XPConnect(); - // Get the xpconnect native call context. - nsAXPCNativeCallContext *cc = nullptr; - xpc->GetCurrentNativeCallContext(&cc); - NS_ENSURE_TRUE(cc, NS_ERROR_INVALID_ARG); - - // Get the current source info from xpc. - nsCOMPtr frame; - xpc->GetCurrentJSStack(getter_AddRefs(frame)); - - // Append the caller's location information. - if (frame) { - nsCString location; - int32_t lineNumber = 0; - frame->GetFilename(getter_Copies(location)); - frame->GetLineNumber(&lineNumber); - - sandboxName.AppendLiteral(" (from: "); - sandboxName.Append(location); - sandboxName.AppendLiteral(":"); - sandboxName.AppendInt(lineNumber); - sandboxName.AppendLiteral(")"); - } - - return NS_OK; -} - -// static -nsresult -nsXPCComponents_utils_Sandbox::CallOrConstruct(nsIXPConnectWrappedNative *wrapper, - JSContext *cx, HandleObject obj, - const CallArgs &args, bool *_retval) -{ - if (args.length() < 1) - return ThrowAndFail(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx, _retval); - - nsresult rv; - - // Make sure to set up principals on the sandbox before initing classes - nsCOMPtr principal; - nsCOMPtr expanded; - nsCOMPtr prinOrSop; - - if (args[0].isString()) { - RootedString str(cx, args[0].toString()); - rv = GetPrincipalFromString(cx, str, getter_AddRefs(principal)); - prinOrSop = principal; - } else if (args[0].isObject()) { - RootedObject obj(cx, &args[0].toObject()); - if (JS_IsArrayObject(cx, obj)) { - rv = GetExpandedPrincipal(cx, obj, getter_AddRefs(expanded)); - prinOrSop = expanded; - } else { - rv = GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop)); - } - } else { - return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); - } - - if (NS_FAILED(rv)) - return ThrowAndFail(rv, cx, _retval); - - SandboxOptions options(cx); - - if (args.length() > 1 && args[1].isObject()) { - if (NS_FAILED(ParseOptionsObject(cx, args[1], options))) - return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); - } - - if (NS_FAILED(AssembleSandboxMemoryReporterName(cx, options.sandboxName))) - return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); - - rv = xpc_CreateSandboxObject(cx, args.rval().address(), prinOrSop, options); - - if (NS_FAILED(rv)) - return ThrowAndFail(rv, cx, _retval); - - *_retval = true; - - return rv; -} - -class ContextHolder : public nsIScriptObjectPrincipal -{ -public: - ContextHolder(JSContext *aOuterCx, HandleObject aSandbox, nsIPrincipal *aPrincipal); - virtual ~ContextHolder(); - - JSContext * GetJSContext() - { - return mJSContext; - } - - nsIPrincipal * GetPrincipal() { return mPrincipal; } - - NS_DECL_ISUPPORTS - -private: - JSContext* mJSContext; - nsCOMPtr mPrincipal; -}; - -NS_IMPL_ISUPPORTS1(ContextHolder, nsIScriptObjectPrincipal) - -ContextHolder::ContextHolder(JSContext *aOuterCx, - HandleObject aSandbox, - nsIPrincipal *aPrincipal) - : mJSContext(JS_NewContext(JS_GetRuntime(aOuterCx), 1024)), - mPrincipal(aPrincipal) -{ - if (mJSContext) { - bool isChrome; - DebugOnly rv = XPCWrapper::GetSecurityManager()-> - IsSystemPrincipal(mPrincipal, &isChrome); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - - JS_SetOptions(mJSContext, - JS_GetOptions(mJSContext) | - JSOPTION_DONT_REPORT_UNCAUGHT | - JSOPTION_PRIVATE_IS_NSISUPPORTS); - js::SetDefaultObjectForContext(mJSContext, aSandbox); - JS_SetContextPrivate(mJSContext, this); - } -} - -ContextHolder::~ContextHolder() -{ - if (mJSContext) - JS_DestroyContextNoGC(mJSContext); -} - -nsresult -xpc_EvalInSandbox(JSContext *cx, HandleObject sandboxArg, const nsAString& source, - const char *filename, int32_t lineNo, - JSVersion jsVersion, bool returnStringOnly, MutableHandleValue rval) -{ - JS_AbortIfWrongThread(JS_GetRuntime(cx)); - rval.set(UndefinedValue()); - - bool waiveXray = xpc::WrapperFactory::HasWaiveXrayFlag(sandboxArg); - RootedObject sandbox(cx, js::CheckedUnwrap(sandboxArg)); - if (!sandbox || js::GetObjectJSClass(sandbox) != &SandboxClass) { - return NS_ERROR_INVALID_ARG; - } - - nsIScriptObjectPrincipal *sop = - (nsIScriptObjectPrincipal*)xpc_GetJSPrivate(sandbox); - MOZ_ASSERT(sop, "Invalid sandbox passed"); - nsCOMPtr prin = sop->GetPrincipal(); - NS_ENSURE_TRUE(prin, NS_ERROR_FAILURE); - - nsAutoCString filenameBuf; - if (!filename) { - // Default to the spec of the principal. - nsJSPrincipals::get(prin)->GetScriptLocation(filenameBuf); - filename = filenameBuf.get(); - lineNo = 1; - } - - // We create a separate cx to do the sandbox evaluation. Scope it. - RootedValue v(cx, UndefinedValue()); - RootedValue exn(cx, UndefinedValue()); - bool ok = true; - { - // Make a special cx for the sandbox and push it. - // NB: As soon as the RefPtr goes away, the cx goes away. So declare - // it first so that it disappears last. - nsRefPtr sandcxHolder = new ContextHolder(cx, sandbox, prin); - JSContext *sandcx = sandcxHolder->GetJSContext(); - if (!sandcx) { - JS_ReportError(cx, "Can't prepare context for evalInSandbox"); - return NS_ERROR_OUT_OF_MEMORY; - } - nsCxPusher pusher; - pusher.Push(sandcx); - JSAutoCompartment ac(sandcx, sandbox); - - JS::CompileOptions options(sandcx); - options.setPrincipals(nsJSPrincipals::get(prin)) - .setFileAndLine(filename, lineNo); - if (jsVersion != JSVERSION_DEFAULT) - options.setVersion(jsVersion); - JS::RootedObject rootedSandbox(sandcx, sandbox); - ok = JS::Evaluate(sandcx, rootedSandbox, options, - PromiseFlatString(source).get(), source.Length(), - v.address()); - if (ok && returnStringOnly && !v.isUndefined()) { - JSString *str = JS_ValueToString(sandcx, v); - ok = !!str; - v = ok ? JS::StringValue(str) : JS::UndefinedValue(); - } - - // If the sandbox threw an exception, grab it off the context. - if (JS_GetPendingException(sandcx, exn.address())) { - MOZ_ASSERT(!ok); - JS_ClearPendingException(sandcx); - if (returnStringOnly) { - // The caller asked for strings only, convert the - // exception into a string. - JSString *str = JS_ValueToString(sandcx, exn); - exn = str ? JS::StringValue(str) : JS::UndefinedValue(); - } - } - } - - // - // Alright, we're back on the caller's cx. If an error occured, try to - // wrap and set the exception. Otherwise, wrap the return value. - // - - if (!ok) { - // If we end up without an exception, it was probably due to OOM along - // the way, in which case we thow. Otherwise, wrap it. - if (exn.isUndefined() || !JS_WrapValue(cx, exn.address())) - return NS_ERROR_OUT_OF_MEMORY; - - // Set the exception on our caller's cx. - JS_SetPendingException(cx, exn); - return NS_ERROR_FAILURE; - } - - // Transitively apply Xray waivers if |sb| was waived. - if (waiveXray) { - ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, v.address()); - } else { - ok = JS_WrapValue(cx, v.address()); - } - NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE); - - // Whew! - rval.set(v); - return NS_OK; -} - -bool -NonCloningFunctionForwarder(JSContext *cx, unsigned argc, Value *vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0)); - MOZ_ASSERT(v.isObject(), "weird function"); - - JSObject *obj = JS_THIS_OBJECT(cx, vp); - if (!obj) { - return false; - } - return JS_CallFunctionValue(cx, obj, v, args.length(), args.array(), vp); -} - -/* - * Forwards the call to the exported function. Clones all the non reflectors, ignores - * the |this| argument. - */ -bool -CloningFunctionForwarder(JSContext *cx, unsigned argc, Value *vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0)); - NS_ASSERTION(v.isObject(), "weird function"); - RootedObject origFunObj(cx, UncheckedUnwrap(&v.toObject())); - { - JSAutoCompartment ac(cx, origFunObj); - // Note: only the arguments are cloned not the |this| or the |callee|. - // Function forwarder does not use those. - for (unsigned i = 0; i < args.length(); i++) { - if (!CloneNonReflectors(cx, args[i])) { - return false; - } - } - - // JS API does not support any JSObject to JSFunction conversion, - // so let's use JS_CallFunctionValue instead. - RootedValue functionVal(cx); - functionVal.setObject(*origFunObj); - - if (!JS_CallFunctionValue(cx, nullptr, functionVal, args.length(), args.array(), vp)) - return false; - } - - // Return value must be wrapped. - return JS_WrapValue(cx, vp); -} - -bool -NewFunctionForwarder(JSContext *cx, HandleId id, HandleObject callable, bool doclone, - MutableHandleValue vp) -{ - JSFunction *fun = js::NewFunctionByIdWithReserved(cx, doclone ? CloningFunctionForwarder : - NonCloningFunctionForwarder, - 0,0, JS::CurrentGlobalOrNull(cx), id); - - if (!fun) - return false; - - JSObject *funobj = JS_GetFunctionObject(fun); - js::SetFunctionNativeReserved(funobj, 0, ObjectValue(*callable)); - vp.setObject(*funobj); - return true; -} diff --git a/js/xpconnect/src/XPCComponents.cpp b/js/xpconnect/src/XPCComponents.cpp index c385e73a336b..1ae811156239 100644 --- a/js/xpconnect/src/XPCComponents.cpp +++ b/js/xpconnect/src/XPCComponents.cpp @@ -40,15 +40,17 @@ #include "nsDOMClassInfoID.h" #include "nsGlobalWindow.h" + using namespace mozilla; using namespace js; using namespace xpc; +using mozilla::dom::DestroyProtoAndIfaceCache; + /***************************************************************************/ // stuff used by all -nsresult -ThrowAndFail(nsresult errNum, JSContext* cx, bool* retval) +static nsresult ThrowAndFail(nsresult errNum, JSContext* cx, bool* retval) { XPCThrower::Throw(errNum, cx); *retval = false; @@ -100,6 +102,7 @@ char * xpc_CheckAccessList(const PRUnichar* wideName, const char* const list[]) /***************************************************************************/ + class nsXPCComponents_Interfaces : public nsIXPCComponents_Interfaces, public nsIXPCScriptable, @@ -2585,6 +2588,27 @@ nsXPCComponents_Constructor::HasInstance(nsIXPConnectWrappedNative *wrapper, return NS_OK; } +/***************************************************************************/ +// Javascript constructor for the sandbox object +class nsXPCComponents_utils_Sandbox : public nsIXPCComponents_utils_Sandbox, + public nsIXPCScriptable +{ +public: + // Aren't macros nice? + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIXPCCOMPONENTS_UTILS_SANDBOX + NS_DECL_NSIXPCSCRIPTABLE + +public: + nsXPCComponents_utils_Sandbox(); + virtual ~nsXPCComponents_utils_Sandbox(); + +private: + static nsresult CallOrConstruct(nsIXPConnectWrappedNative *wrapper, + JSContext *cx, HandleObject obj, + const CallArgs &args, bool *_retval); +}; + class nsXPCComponents_Utils : public nsIXPCComponents_Utils, public nsIXPCScriptable, @@ -2625,9 +2649,10 @@ NS_IMETHODIMP nsXPCComponents_Utils::GetSandbox(nsIXPCComponents_utils_Sandbox **aSandbox) { NS_ENSURE_ARG_POINTER(aSandbox); - if (!mSandbox) - mSandbox = NewSandboxConstructor(); - + if (!mSandbox && !(mSandbox = new nsXPCComponents_utils_Sandbox())) { + *aSandbox = nullptr; + return NS_ERROR_OUT_OF_MEMORY; + } NS_ADDREF(*aSandbox = mSandbox); return NS_OK; } @@ -2768,6 +2793,1344 @@ nsXPCComponents_Utils::ReportError(const JS::Value &errorArg, JSContext *cx) return NS_OK; } +#include "nsIScriptSecurityManager.h" +#include "nsIURI.h" +#include "nsNetUtil.h" +const char kScriptSecurityManagerContractID[] = NS_SCRIPTSECURITYMANAGER_CONTRACTID; + +NS_IMPL_ISUPPORTS3(SandboxPrivate, + nsIScriptObjectPrincipal, + nsIGlobalObject, + nsISupportsWeakReference) + +static bool +SandboxDump(JSContext *cx, unsigned argc, jsval *vp) +{ + JSString *str; + if (!argc) + return true; + + str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]); + if (!str) + return false; + + size_t length; + const jschar *chars = JS_GetStringCharsZAndLength(cx, str, &length); + if (!chars) + return false; + + nsDependentString wstr(chars, length); + char *cstr = ToNewUTF8String(wstr); + if (!cstr) + return false; + +#if defined(XP_MACOSX) + // Be nice and convert all \r to \n. + char *c = cstr, *cEnd = cstr + strlen(cstr); + while (c < cEnd) { + if (*c == '\r') + *c = '\n'; + c++; + } +#endif + + fputs(cstr, stdout); + fflush(stdout); + NS_Free(cstr); + JS_SET_RVAL(cx, vp, JSVAL_TRUE); + return true; +} + +static bool +SandboxDebug(JSContext *cx, unsigned argc, jsval *vp) +{ +#ifdef DEBUG + return SandboxDump(cx, argc, vp); +#else + return true; +#endif +} + +static bool +SandboxImport(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + if (args.length() < 1 || args[0].isPrimitive()) { + XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx); + return false; + } + + RootedString funname(cx); + if (args.length() > 1) { + // Use the second parameter as the function name. + funname = JS_ValueToString(cx, args[1]); + if (!funname) + return false; + } else { + // NB: funobj must only be used to get the JSFunction out. + RootedObject funobj(cx, &args[0].toObject()); + if (js::IsProxy(funobj)) { + funobj = XPCWrapper::UnsafeUnwrapSecurityWrapper(funobj); + } + + JSAutoCompartment ac(cx, funobj); + + JSFunction *fun = JS_ValueToFunction(cx, ObjectValue(*funobj)); + if (!fun) { + XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx); + return false; + } + + // Use the actual function name as the name. + funname = JS_GetFunctionId(fun); + if (!funname) { + XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx); + return false; + } + } + + RootedId id(cx); + if (!JS_ValueToId(cx, StringValue(funname), id.address())) + return false; + + // We need to resolve the this object, because this function is used + // unbound and should still work and act on the original sandbox. + RootedObject thisObject(cx, JS_THIS_OBJECT(cx, vp)); + if (!thisObject) { + XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx); + return false; + } + if (!JS_SetPropertyById(cx, thisObject, id, args[0])) + return false; + + args.rval().setUndefined(); + return true; +} + +static bool +CreateXMLHttpRequest(JSContext *cx, unsigned argc, jsval *vp) +{ + nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); + if (!ssm) + return false; + + nsIPrincipal *subjectPrincipal = ssm->GetCxSubjectPrincipal(cx); + if (!subjectPrincipal) + return false; + + RootedObject global(cx, JS::CurrentGlobalOrNull(cx)); + MOZ_ASSERT(global); + + nsIScriptObjectPrincipal *sop = + static_cast(xpc_GetJSPrivate(global)); + nsCOMPtr iglobal = do_QueryInterface(sop); + + nsCOMPtr xhr = new nsXMLHttpRequest(); + nsresult rv = xhr->Init(subjectPrincipal, nullptr, iglobal, nullptr); + if (NS_FAILED(rv)) + return false; + + rv = nsContentUtils::WrapNative(cx, global, xhr, vp); + if (NS_FAILED(rv)) + return false; + + return true; +} + +bool +NewFunctionForwarder(JSContext *cx, HandleId id, HandleObject callable, + bool doclone, MutableHandleValue vp); + +/* + * Instead of simply wrapping a function into another compartment, + * this helper function creates a native function in the target + * compartment and forwards the call to the original function. + * That call will be different than a regular JS function call in + * that, the |this| is left unbound, and all the non-native JS + * object arguments will be cloned using the structured clone + * algorithm. + * The return value is the new forwarder function, wrapped into + * the caller's compartment. + * The 3rd argument is the name of the property that will + * be set on the target scope, with the forwarder function as + * the value. + * The principal of the caller must subsume that of the target. + * + * Expected type of the arguments and the return value: + * function exportFunction(function funToExport, + * object targetScope, + * string name) + */ +static bool +ExportFunction(JSContext *cx, unsigned argc, jsval *vp) +{ + MOZ_ASSERT(cx); + if (argc < 3) { + JS_ReportError(cx, "Function requires at least 3 arguments"); + return false; + } + + CallArgs args = CallArgsFromVp(argc, vp); + if (!args[0].isObject() || !args[1].isObject() || !args[2].isString()) { + JS_ReportError(cx, "Invalid argument"); + return false; + } + + RootedObject funObj(cx, &args[0].toObject()); + RootedObject targetScope(cx, &args[1].toObject()); + RootedString funName(cx, args[2].toString()); + + // We can only export functions to scopes those are transparent for us, + // so if there is a security wrapper around targetScope we must throw. + targetScope = CheckedUnwrap(targetScope); + if (!targetScope) { + JS_ReportError(cx, "Permission denied to export function into scope"); + return false; + } + + if (JS_GetStringLength(funName) == 0) { + JS_ReportError(cx, "3rd argument should be a non-empty string"); + return false; + } + + { + // We need to operate in the target scope from here on, let's enter + // its compartment. + JSAutoCompartment ac(cx, targetScope); + + // Unwrapping to see if we have a callable. + funObj = UncheckedUnwrap(funObj); + if (!JS_ObjectIsCallable(cx, funObj)) { + JS_ReportError(cx, "First argument must be a function"); + return false; + } + + // The function forwarder will live in the target compartment. Since + // this function will be referenced from its private slot, to avoid a + // GC hazard, we must wrap it to the same compartment. + if (!JS_WrapObject(cx, funObj.address())) + return false; + + RootedId id(cx); + if (!JS_ValueToId(cx, args[2], id.address())) + return false; + + // And now, let's create the forwarder function in the target compartment + // for the function the be exported. + if (!NewFunctionForwarder(cx, id, funObj, /* doclone = */ true, args.rval())) { + JS_ReportError(cx, "Exporting function failed"); + return false; + } + + // We have the forwarder function in the target compartment, now + // we have to add it to the target scope as a property. + if (!JS_DefinePropertyById(cx, targetScope, id, args.rval(), + JS_PropertyStub, JS_StrictPropertyStub, + JSPROP_ENUMERATE)) + return false; + } + + // Finally we have to re-wrap the exported function back to the caller compartment. + if (!JS_WrapValue(cx, args.rval().address())) + return false; + + return true; +} + +static bool +GetFilenameAndLineNumber(JSContext *cx, nsACString &filename, unsigned &lineno) +{ + JSScript *script; + if (JS_DescribeScriptedCaller(cx, &script, &lineno)) { + if (const char *cfilename = JS_GetScriptFilename(cx, script)) { + filename.Assign(nsDependentCString(cfilename)); + return true; + } + } + return false; +} + +namespace xpc { +bool +IsReflector(JSObject *obj) +{ + return IS_WN_REFLECTOR(obj) || dom::IsDOMObject(obj); +} +} /* namespace xpc */ + +enum ForwarderCloneTags { + SCTAG_BASE = JS_SCTAG_USER_MIN, + SCTAG_REFLECTOR +}; + +static JSObject * +CloneNonReflectorsRead(JSContext *cx, JSStructuredCloneReader *reader, uint32_t tag, + uint32_t data, void *closure) +{ + MOZ_ASSERT(closure, "Null pointer!"); + AutoObjectVector *reflectors = static_cast(closure); + if (tag == SCTAG_REFLECTOR) { + MOZ_ASSERT(!data); + + size_t idx; + if (JS_ReadBytes(reader, &idx, sizeof(size_t))) { + RootedObject reflector(cx, reflectors->handleAt(idx)); + MOZ_ASSERT(reflector, "No object pointer?"); + MOZ_ASSERT(IsReflector(reflector), "Object pointer must be a reflector!"); + + JS_WrapObject(cx, reflector.address()); + JS_ASSERT(WrapperFactory::IsXrayWrapper(reflector) || + IsReflector(reflector)); + + return reflector; + } + } + + JS_ReportError(cx, "CloneNonReflectorsRead error"); + return nullptr; +} + +static bool +CloneNonReflectorsWrite(JSContext *cx, JSStructuredCloneWriter *writer, + Handle obj, void *closure) +{ + MOZ_ASSERT(closure, "Null pointer!"); + + // We need to maintain a list of reflectors to make sure all these objects + // are properly rooter. Only their indices will be serialized. + AutoObjectVector *reflectors = static_cast(closure); + if (IsReflector(obj)) { + if (!reflectors->append(obj)) + return false; + + size_t idx = reflectors->length()-1; + if (JS_WriteUint32Pair(writer, SCTAG_REFLECTOR, 0) && + JS_WriteBytes(writer, &idx, sizeof(size_t))) { + return true; + } + } + + JS_ReportError(cx, "CloneNonReflectorsWrite error"); + return false; +} + +JSStructuredCloneCallbacks gForwarderStructuredCloneCallbacks = { + CloneNonReflectorsRead, + CloneNonReflectorsWrite, + nullptr +}; + +/* + * This is a special structured cloning, that clones only non-reflectors. + * The function assumes the cx is already entered the compartment we want + * to clone to, and that if val is an object is from the compartment we + * clone from. + */ +bool +CloneNonReflectors(JSContext *cx, MutableHandleValue val) +{ + JSAutoStructuredCloneBuffer buffer; + AutoObjectVector rootedReflectors(cx); + { + // For parsing val we have to enter its compartment. + // (unless it's a primitive) + Maybe ac; + if (val.isObject()) { + ac.construct(cx, &val.toObject()); + } + + if (!buffer.write(cx, val, + &gForwarderStructuredCloneCallbacks, + &rootedReflectors)) + { + return false; + } + } + + // Now recreate the clones in the target compartment. + RootedValue rval(cx); + if (!buffer.read(cx, val.address(), + &gForwarderStructuredCloneCallbacks, + &rootedReflectors)) + { + return false; + } + + return true; +} + +/* + * Similar to evalInSandbox except this one is used to eval a script in the + * scope of a window. Also note, that the return value and the possible exceptions + * in the script are structured cloned, unless they are natives (then they are just + * wrapped). + * Principal of the caller must subsume the target's. + * + * Expected type of the arguments: + * value evalInWindow(string script, + * object window) + */ +static bool +EvalInWindow(JSContext *cx, unsigned argc, jsval *vp) +{ + MOZ_ASSERT(cx); + if (argc < 2) { + JS_ReportError(cx, "Function requires two arguments"); + return false; + } + + CallArgs args = CallArgsFromVp(argc, vp); + if (!args[0].isString() || !args[1].isObject()) { + JS_ReportError(cx, "Invalid arguments"); + return false; + } + + RootedString srcString(cx, args[0].toString()); + RootedObject targetScope(cx, &args[1].toObject()); + + // If we cannot unwrap we must not eval in it. + targetScope = CheckedUnwrap(targetScope); + if (!targetScope) { + JS_ReportError(cx, "Permission denied to eval in target scope"); + return false; + } + + // Make sure that we have a window object. + RootedObject inner(cx, CheckedUnwrap(targetScope, /* stopAtOuter = */ false)); + nsCOMPtr global; + nsCOMPtr window; + if (!JS_IsGlobalObject(inner) || + !(global = GetNativeForGlobal(inner)) || + !(window = do_QueryInterface(global))) + { + JS_ReportError(cx, "Second argument must be a window"); + return false; + } + + nsCOMPtr context = + (static_cast(window.get()))->GetScriptContext(); + if (!context) { + JS_ReportError(cx, "Script context needed"); + return false; + } + + if (!context->GetScriptsEnabled()) { + JS_ReportError(cx, "Scripts are disabled in this window"); + return false; + } + + nsCString filename; + unsigned lineNo; + if (!GetFilenameAndLineNumber(cx, filename, lineNo)) { + // Default values for non-scripted callers. + filename.Assign("Unknown"); + lineNo = 0; + } + + nsDependentJSString srcDepString; + srcDepString.init(cx, srcString); + + { + // CompileOptions must be created from the context + // we will execute this script in. + JSContext *wndCx = context->GetNativeContext(); + AutoCxPusher pusher(wndCx); + JS::CompileOptions compileOptions(wndCx); + compileOptions.setFileAndLine(filename.get(), lineNo); + + // We don't want the JS engine to automatically report + // uncaught exceptions. + nsJSUtils::EvaluateOptions evaluateOptions; + evaluateOptions.setReportUncaught(false); + + nsresult rv = nsJSUtils::EvaluateString(wndCx, + srcDepString, + targetScope, + compileOptions, + evaluateOptions, + args.rval().address()); + + if (NS_FAILED(rv)) { + // If there was an exception we get it as a return value, if + // the evaluation failed for some other reason, then a default + // exception is raised. + MOZ_ASSERT(!JS_IsExceptionPending(wndCx), + "Exception should be delivered as return value."); + if (args.rval().isUndefined()) { + MOZ_ASSERT(rv == NS_ERROR_OUT_OF_MEMORY); + return false; + } + + // If there was an exception thrown we should set it + // on the calling context. + RootedValue exn(wndCx, args.rval()); + // First we should reset the return value. + args.rval().set(UndefinedValue()); + + // Then clone the exception. + if (CloneNonReflectors(cx, &exn)) + JS_SetPendingException(cx, exn); + + return false; + } + } + + // Let's clone the return value back to the callers compartment. + if (!CloneNonReflectors(cx, args.rval())) { + args.rval().set(UndefinedValue()); + return false; + } + + return true; +} + +static bool +sandbox_enumerate(JSContext *cx, HandleObject obj) +{ + return JS_EnumerateStandardClasses(cx, obj); +} + +static bool +sandbox_resolve(JSContext *cx, HandleObject obj, HandleId id) +{ + bool resolved; + return JS_ResolveStandardClass(cx, obj, id, &resolved); +} + +static void +sandbox_finalize(JSFreeOp *fop, JSObject *obj) +{ + nsIScriptObjectPrincipal *sop = + static_cast(xpc_GetJSPrivate(obj)); + MOZ_ASSERT(sop); + static_cast(sop)->ForgetGlobalObject(); + NS_IF_RELEASE(sop); + DestroyProtoAndIfaceCache(obj); +} + +static bool +sandbox_convert(JSContext *cx, HandleObject obj, JSType type, MutableHandleValue vp) +{ + if (type == JSTYPE_OBJECT) { + vp.set(OBJECT_TO_JSVAL(obj)); + return true; + } + + return JS_ConvertStub(cx, obj, type, vp); +} + +static JSClass SandboxClass = { + "Sandbox", + XPCONNECT_GLOBAL_FLAGS, + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, + sandbox_enumerate, sandbox_resolve, sandbox_convert, sandbox_finalize, + NULL, NULL, NULL, NULL, TraceXPCGlobal +}; + +static const JSFunctionSpec SandboxFunctions[] = { + JS_FS("dump", SandboxDump, 1,0), + JS_FS("debug", SandboxDebug, 1,0), + JS_FS("importFunction", SandboxImport, 1,0), + JS_FS_END +}; + +/***************************************************************************/ +nsXPCComponents_utils_Sandbox::nsXPCComponents_utils_Sandbox() +{ +} + +nsXPCComponents_utils_Sandbox::~nsXPCComponents_utils_Sandbox() +{ +} + +NS_INTERFACE_MAP_BEGIN(nsXPCComponents_utils_Sandbox) + NS_INTERFACE_MAP_ENTRY(nsIXPCComponents_utils_Sandbox) + NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_utils_Sandbox) +NS_INTERFACE_MAP_END_THREADSAFE + +NS_IMPL_ADDREF(nsXPCComponents_utils_Sandbox) +NS_IMPL_RELEASE(nsXPCComponents_utils_Sandbox) + +// We use the nsIXPScriptable macros to generate lots of stuff for us. +#define XPC_MAP_CLASSNAME nsXPCComponents_utils_Sandbox +#define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_utils_Sandbox" +#define XPC_MAP_WANT_CALL +#define XPC_MAP_WANT_CONSTRUCT +#define XPC_MAP_FLAGS 0 +#include "xpc_map_end.h" /* This #undef's the above. */ + +xpc::SandboxProxyHandler xpc::sandboxProxyHandler; + +bool +xpc::IsSandboxPrototypeProxy(JSObject *obj) +{ + return js::IsProxy(obj) && + js::GetProxyHandler(obj) == &xpc::sandboxProxyHandler; +} + +bool +xpc::SandboxCallableProxyHandler::call(JSContext *cx, JS::Handle proxy, + const JS::CallArgs &args) +{ + // We forward the call to our underlying callable. + + // The parent of our proxy is the SandboxProxyHandler proxy + RootedObject sandboxProxy(cx, JS_GetParent(proxy)); + MOZ_ASSERT(js::IsProxy(sandboxProxy) && + js::GetProxyHandler(sandboxProxy) == &xpc::sandboxProxyHandler); + + // The parent of the sandboxProxy is the sandbox global, and the + // target object is the original proto. + RootedObject sandboxGlobal(cx, JS_GetParent(sandboxProxy)); + MOZ_ASSERT(js::GetObjectJSClass(sandboxGlobal) == &SandboxClass); + + // If our this object is the sandbox global, we call with this set to the + // original proto instead. + // + // There are two different ways we can compute |this|. If we use + // JS_THIS_VALUE, we'll get the bonafide |this| value as passed by the + // caller, which may be undefined if a global function was invoked without + // an explicit invocant. If we use JS_THIS or JS_THIS_OBJECT, the |this| + // in |vp| will be coerced to the global, which is not the correct + // behavior in ES5 strict mode. And we have no way to compute strictness + // here. + // + // The naive approach is simply to use JS_THIS_VALUE here. If |this| was + // explicit, we can remap it appropriately. If it was implicit, then we + // leave it as undefined, and let the callee sort it out. Since the callee + // is generally in the same compartment as its global (eg the Window's + // compartment, not the Sandbox's), the callee will generally compute the + // correct |this|. + // + // However, this breaks down in the Xray case. If the sandboxPrototype + // is an Xray wrapper, then we'll end up reifying the native methods in + // the Sandbox's scope, which means that they'll compute |this| to be the + // Sandbox, breaking old-style XPC_WN_CallMethod methods. + // + // Luckily, the intent of Xrays is to provide a vanilla view of a foreign + // DOM interface, which means that we don't care about script-enacted + // strictness in the prototype's home compartment. Indeed, since DOM + // methods are always non-strict, we can just assume non-strict semantics + // if the sandboxPrototype is an Xray Wrapper, which lets us appropriately + // remap |this|. + JS::Value thisVal = + WrapperFactory::IsXrayWrapper(sandboxProxy) ? args.computeThis(cx) : args.thisv(); + if (thisVal == ObjectValue(*sandboxGlobal)) { + thisVal = ObjectValue(*js::GetProxyTargetObject(sandboxProxy)); + } + + return JS::Call(cx, thisVal, js::GetProxyPrivate(proxy), args.length(), args.array(), + args.rval()); +} + +xpc::SandboxCallableProxyHandler xpc::sandboxCallableProxyHandler; + +// Wrap a callable such that if we're called with oldThisObj as the +// "this" we will instead call it with newThisObj as the this. +static JSObject* +WrapCallable(JSContext *cx, JSObject *callable, JSObject *sandboxProtoProxy) +{ + MOZ_ASSERT(JS_ObjectIsCallable(cx, callable)); + // Our proxy is wrapping the callable. So we need to use the + // callable as the private. We use the given sandboxProtoProxy as + // the parent, and our call() hook depends on that. + MOZ_ASSERT(js::IsProxy(sandboxProtoProxy) && + js::GetProxyHandler(sandboxProtoProxy) == + &xpc::sandboxProxyHandler); + + RootedValue priv(cx, ObjectValue(*callable)); + return js::NewProxyObject(cx, &xpc::sandboxCallableProxyHandler, + priv, nullptr, + sandboxProtoProxy, js::ProxyIsCallable); +} + +template +bool BindPropertyOp(JSContext *cx, Op &op, JSPropertyDescriptor *desc, HandleId id, + unsigned attrFlag, HandleObject sandboxProtoProxy) +{ + if (!op) { + return true; + } + + RootedObject func(cx); + if (desc->attrs & attrFlag) { + // Already an object + func = JS_FUNC_TO_DATA_PTR(JSObject *, op); + } else { + // We have an actual property op. For getters, we use 0 + // args, for setters we use 1 arg. + uint32_t args = (attrFlag == JSPROP_GETTER) ? 0 : 1; + RootedObject obj(cx, desc->obj); + func = GeneratePropertyOp(cx, obj, id, args, op); + if (!func) + return false; + } + func = WrapCallable(cx, func, sandboxProtoProxy); + if (!func) + return false; + op = JS_DATA_TO_FUNC_PTR(Op, func.get()); + desc->attrs |= attrFlag; + return true; +} + +extern bool +XPC_WN_Helper_GetProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp); +extern bool +XPC_WN_Helper_SetProperty(JSContext *cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp); + +bool +xpc::SandboxProxyHandler::getPropertyDescriptor(JSContext *cx, + JS::Handle proxy, + JS::Handle id, + JS::MutableHandle desc, + unsigned flags) +{ + JS::RootedObject obj(cx, wrappedObject(proxy)); + + MOZ_ASSERT(js::GetObjectCompartment(obj) == js::GetObjectCompartment(proxy)); + if (!JS_GetPropertyDescriptorById(cx, obj, id, + flags, desc)) + return false; + + if (!desc.object()) + return true; // No property, nothing to do + + // Now fix up the getter/setter/value as needed to be bound to desc->obj + // Don't mess with holder_get and holder_set, though, because those rely on + // the "vp is prefilled with the value in the slot" behavior that property + // ops can in theory rely on, but our property op forwarder doesn't know how + // to make that happen. Since we really only need to rebind the DOM methods + // here, not rebindings holder_get and holder_set is OK. + // + // Similarly, don't mess with XPC_WN_Helper_GetProperty and + // XPC_WN_Helper_SetProperty, for the same reasons: that could confuse our + // access to expandos when we're not doing Xrays. + if (desc.getter() != xpc::holder_get && + desc.getter() != XPC_WN_Helper_GetProperty && + !BindPropertyOp(cx, desc.getter(), desc.address(), id, JSPROP_GETTER, proxy)) + return false; + if (desc.setter() != xpc::holder_set && + desc.setter() != XPC_WN_Helper_SetProperty && + !BindPropertyOp(cx, desc.setter(), desc.address(), id, JSPROP_SETTER, proxy)) + return false; + if (desc.value().isObject()) { + JSObject* val = &desc.value().toObject(); + if (JS_ObjectIsCallable(cx, val)) { + val = WrapCallable(cx, val, proxy); + if (!val) + return false; + desc.value().setObject(*val); + } + } + + return true; +} + +bool +xpc::SandboxProxyHandler::getOwnPropertyDescriptor(JSContext *cx, + JS::Handle proxy, + JS::Handle id, + JS::MutableHandle desc, + unsigned flags) +{ + if (!getPropertyDescriptor(cx, proxy, id, desc, flags)) + return false; + + if (desc.object() != wrappedObject(proxy)) + desc.object().set(nullptr); + + return true; +} + +/* + * Reuse the BaseProxyHandler versions of the derived traps that are implemented + * in terms of the fundamental traps. + */ + +bool +xpc::SandboxProxyHandler::has(JSContext *cx, JS::Handle proxy, + JS::Handle id, bool *bp) +{ + return BaseProxyHandler::has(cx, proxy, id, bp); +} +bool +xpc::SandboxProxyHandler::hasOwn(JSContext *cx, JS::Handle proxy, + JS::Handle id, bool *bp) +{ + return BaseProxyHandler::hasOwn(cx, proxy, id, bp); +} + +bool +xpc::SandboxProxyHandler::get(JSContext *cx, JS::Handle proxy, + JS::Handle receiver, + JS::Handle id, + JS::MutableHandle vp) +{ + return BaseProxyHandler::get(cx, proxy, receiver, id, vp); +} + +bool +xpc::SandboxProxyHandler::set(JSContext *cx, JS::Handle proxy, + JS::Handle receiver, + JS::Handle id, + bool strict, + JS::MutableHandle vp) +{ + return BaseProxyHandler::set(cx, proxy, receiver, id, strict, vp); +} + +bool +xpc::SandboxProxyHandler::keys(JSContext *cx, JS::Handle proxy, + AutoIdVector &props) +{ + return BaseProxyHandler::keys(cx, proxy, props); +} + +bool +xpc::SandboxProxyHandler::iterate(JSContext *cx, JS::Handle proxy, + unsigned flags, JS::MutableHandle vp) +{ + return BaseProxyHandler::iterate(cx, proxy, flags, vp); +} + +nsresult +xpc_CreateSandboxObject(JSContext *cx, jsval *vp, nsISupports *prinOrSop, SandboxOptions& options) +{ + // Create the sandbox global object + nsresult rv; + nsCOMPtr xpc(do_GetService(nsIXPConnect::GetCID(), &rv)); + if (NS_FAILED(rv)) + return NS_ERROR_XPC_UNEXPECTED; + + nsCOMPtr principal = do_QueryInterface(prinOrSop); + if (!principal) { + nsCOMPtr sop = do_QueryInterface(prinOrSop); + if (sop) { + principal = sop->GetPrincipal(); + } else { + principal = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv); + MOZ_ASSERT(NS_FAILED(rv) || principal, "Bad return from do_CreateInstance"); + + if (!principal || NS_FAILED(rv)) { + if (NS_SUCCEEDED(rv)) { + rv = NS_ERROR_FAILURE; + } + + return rv; + } + } + MOZ_ASSERT(principal); + } + + JS::CompartmentOptions compartmentOptions; + compartmentOptions.setZone(options.sameZoneAs + ? JS::SameZoneAs(js::UncheckedUnwrap(options.sameZoneAs)) + : JS::SystemZone); + RootedObject sandbox(cx, xpc::CreateGlobalObject(cx, &SandboxClass, + principal, compartmentOptions)); + if (!sandbox) + return NS_ERROR_FAILURE; + + // Set up the wantXrays flag, which indicates whether xrays are desired even + // for same-origin access. + // + // This flag has historically been ignored for chrome sandboxes due to + // quirks in the wrapping implementation that have now been removed. Indeed, + // same-origin Xrays for chrome->chrome access seems a bit superfluous. + // Arguably we should just flip the default for chrome and still honor the + // flag, but such a change would break code in subtle ways for minimal + // benefit. So we just switch it off here. + xpc::GetCompartmentPrivate(sandbox)->wantXrays = + AccessCheck::isChrome(sandbox) ? false : options.wantXrays; + + { + JSAutoCompartment ac(cx, sandbox); + + if (options.proto) { + bool ok = JS_WrapObject(cx, options.proto.address()); + if (!ok) + return NS_ERROR_XPC_UNEXPECTED; + + if (xpc::WrapperFactory::IsXrayWrapper(options.proto) && !options.wantXrays) { + RootedValue v(cx, ObjectValue(*options.proto)); + if (!xpc::WrapperFactory::WaiveXrayAndWrap(cx, v.address())) + return NS_ERROR_FAILURE; + options.proto = &v.toObject(); + } + + // Now check what sort of thing we've got in |proto| + JSObject *unwrappedProto = js::UncheckedUnwrap(options.proto, false); + js::Class *unwrappedClass = js::GetObjectClass(unwrappedProto); + if (IS_WN_CLASS(unwrappedClass) || + mozilla::dom::IsDOMClass(Jsvalify(unwrappedClass))) { + // Wrap it up in a proxy that will do the right thing in terms + // of this-binding for methods. + RootedValue priv(cx, ObjectValue(*options.proto)); + options.proto = js::NewProxyObject(cx, &xpc::sandboxProxyHandler, + priv, nullptr, sandbox); + if (!options.proto) + return NS_ERROR_OUT_OF_MEMORY; + } + + ok = JS_SetPrototype(cx, sandbox, options.proto); + if (!ok) + return NS_ERROR_XPC_UNEXPECTED; + } + + nsCOMPtr sbp = + new SandboxPrivate(principal, sandbox); + + // Pass on ownership of sbp to |sandbox|. + JS_SetPrivate(sandbox, sbp.forget().get()); + + bool allowComponents = nsContentUtils::IsSystemPrincipal(principal) || + nsContentUtils::IsExpandedPrincipal(principal); + if (options.wantComponents && allowComponents && + !nsXPCComponents::AttachComponentsObject(cx, GetObjectScope(sandbox))) + return NS_ERROR_XPC_UNEXPECTED; + + if (!XPCNativeWrapper::AttachNewConstructorObject(cx, sandbox)) + return NS_ERROR_XPC_UNEXPECTED; + + if (!JS_DefineFunctions(cx, sandbox, SandboxFunctions)) + return NS_ERROR_XPC_UNEXPECTED; + + if (options.wantXHRConstructor && + !JS_DefineFunction(cx, sandbox, "XMLHttpRequest", CreateXMLHttpRequest, 0, JSFUN_CONSTRUCTOR)) + return NS_ERROR_XPC_UNEXPECTED; + + if (options.wantExportHelpers && + (!JS_DefineFunction(cx, sandbox, "exportFunction", ExportFunction, 3, 0) || + !JS_DefineFunction(cx, sandbox, "evalInWindow", EvalInWindow, 2, 0))) + return NS_ERROR_XPC_UNEXPECTED; + + } + + if (vp) { + // We have this crazy behavior where wantXrays=false also implies that the + // returned sandbox is implicitly waived. We've stopped advertising it, but + // keep supporting it for now. + *vp = OBJECT_TO_JSVAL(sandbox); + if (options.wantXrays && !JS_WrapValue(cx, vp)) + return NS_ERROR_UNEXPECTED; + if (!options.wantXrays && !xpc::WrapperFactory::WaiveXrayAndWrap(cx, vp)) + return NS_ERROR_UNEXPECTED; + } + + // Set the location information for the new global, so that tools like + // about:memory may use that information + xpc::SetLocationForGlobal(sandbox, options.sandboxName); + + JS_FireOnNewGlobalObject(cx, sandbox); + + return NS_OK; +} + +/* bool call(in nsIXPConnectWrappedNative wrapper, + in JSContextPtr cx, + in JSObjectPtr obj, + in uint32_t argc, + in JSValPtr argv, + in JSValPtr vp); +*/ +NS_IMETHODIMP +nsXPCComponents_utils_Sandbox::Call(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *objArg, const CallArgs &args, bool *_retval) +{ + RootedObject obj(cx, objArg); + return CallOrConstruct(wrapper, cx, obj, args, _retval); +} + +/* bool construct(in nsIXPConnectWrappedNative wrapper, + in JSContextPtr cx, + in JSObjectPtr obj, + in uint32_t argc, + in JSValPtr argv, + in JSValPtr vp); +*/ +NS_IMETHODIMP +nsXPCComponents_utils_Sandbox::Construct(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *objArg, const CallArgs &args, bool *_retval) +{ + RootedObject obj(cx, objArg); + return CallOrConstruct(wrapper, cx, obj, args, _retval); +} + +// for sandbox constructor the first argument can be a URI string in which case +// we use the related Codebase Principal for the sandbox +nsresult +GetPrincipalFromString(JSContext *cx, HandleString codebase, nsIPrincipal **principal) +{ + MOZ_ASSERT(principal); + MOZ_ASSERT(codebase); + nsCOMPtr uri; + nsDependentJSString codebaseStr; + NS_ENSURE_TRUE(codebaseStr.init(cx, codebase), NS_ERROR_FAILURE); + nsresult rv = NS_NewURI(getter_AddRefs(uri), codebaseStr); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr secman = + do_GetService(kScriptSecurityManagerContractID); + NS_ENSURE_TRUE(secman, NS_ERROR_FAILURE); + + // We could allow passing in the app-id and browser-element info to the + // sandbox constructor. But creating a sandbox based on a string is a + // deprecated API so no need to add features to it. + rv = secman->GetNoAppCodebasePrincipal(uri, principal); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(*principal, NS_ERROR_FAILURE); + + return NS_OK; +} + +// for sandbox constructor the first argument can be a principal object or +// a script object principal (Document, Window) +nsresult +GetPrincipalOrSOP(JSContext *cx, HandleObject from, nsISupports **out) +{ + MOZ_ASSERT(out); + *out = NULL; + + nsXPConnect* xpc = nsXPConnect::XPConnect(); + nsCOMPtr wrapper; + xpc->GetWrappedNativeOfJSObject(cx, from, + getter_AddRefs(wrapper)); + + NS_ENSURE_TRUE(wrapper, NS_ERROR_INVALID_ARG); + + if (nsCOMPtr sop = do_QueryWrappedNative(wrapper)) { + sop.forget(out); + return NS_OK; + } + + nsCOMPtr principal = do_QueryWrappedNative(wrapper); + principal.forget(out); + NS_ENSURE_TRUE(*out, NS_ERROR_INVALID_ARG); + + return NS_OK; +} + +// the first parameter of the sandbox constructor might be an array of principals, either in string +// format or actual objects (see GetPrincipalOrSOP) +nsresult +GetExpandedPrincipal(JSContext *cx, HandleObject arrayObj, nsIExpandedPrincipal **out) +{ + MOZ_ASSERT(out); + uint32_t length; + + if (!JS_IsArrayObject(cx, arrayObj) || + !JS_GetArrayLength(cx, arrayObj, &length) || + !length) + { + // we need a white list of principals or uri strings to create an + // expanded principal, if we got an empty array or something else + // report error + return NS_ERROR_INVALID_ARG; + } + + nsTArray< nsCOMPtr > allowedDomains(length); + allowedDomains.SetLength(length); + nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); + NS_ENSURE_TRUE(ssm, NS_ERROR_XPC_UNEXPECTED); + + for (uint32_t i = 0; i < length; ++i) { + RootedValue allowed(cx); + if (!JS_GetElement(cx, arrayObj, i, &allowed)) + return NS_ERROR_INVALID_ARG; + + nsresult rv; + nsCOMPtr principal; + if (allowed.isString()) { + // in case of string let's try to fetch a codebase principal from it + RootedString str(cx, allowed.toString()); + rv = GetPrincipalFromString(cx, str, getter_AddRefs(principal)); + NS_ENSURE_SUCCESS(rv, rv); + } else if (allowed.isObject()) { + // in case of object let's see if it's a Principal or a ScriptObjectPrincipal + nsCOMPtr prinOrSop; + RootedObject obj(cx, &allowed.toObject()); + rv = GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr sop(do_QueryInterface(prinOrSop)); + principal = do_QueryInterface(prinOrSop); + if (sop) { + principal = sop->GetPrincipal(); + } + } + NS_ENSURE_TRUE(principal, NS_ERROR_INVALID_ARG); + + // We do not allow ExpandedPrincipals to contain any system principals + bool isSystem; + rv = ssm->IsSystemPrincipal(principal, &isSystem); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_FALSE(isSystem, NS_ERROR_INVALID_ARG); + allowedDomains[i] = principal; + } + + nsCOMPtr result = new nsExpandedPrincipal(allowedDomains); + result.forget(out); + return NS_OK; +} + +// helper that tries to get a property form the options object +nsresult +GetPropFromOptions(JSContext *cx, HandleObject from, const char *name, MutableHandleValue prop, + bool *found) +{ + if (!JS_HasProperty(cx, from, name, found)) + return NS_ERROR_INVALID_ARG; + + if (found && !JS_GetProperty(cx, from, name, prop)) + return NS_ERROR_INVALID_ARG; + + return NS_OK; +} + +// helper that tries to get a boolean property form the options object +nsresult +GetBoolPropFromOptions(JSContext *cx, HandleObject from, const char *name, bool *prop) +{ + MOZ_ASSERT(prop); + + + RootedValue value(cx); + bool found; + if (NS_FAILED(GetPropFromOptions(cx, from, name, &value, &found))) + return NS_ERROR_INVALID_ARG; + + if (!found) + return NS_OK; + + if (!value.isBoolean()) + return NS_ERROR_INVALID_ARG; + + *prop = value.toBoolean(); + return NS_OK; +} + +// helper that tries to get an object property form the options object +nsresult +GetObjPropFromOptions(JSContext *cx, HandleObject from, const char *name, JSObject **prop) +{ + MOZ_ASSERT(prop); + + RootedValue value(cx); + bool found; + if (NS_FAILED(GetPropFromOptions(cx, from, name, &value, &found))) + return NS_ERROR_INVALID_ARG; + + if (!found) { + *prop = NULL; + return NS_OK; + } + + if (!value.isObject()) + return NS_ERROR_INVALID_ARG; + + *prop = &value.toObject(); + return NS_OK; +} + +// helper that tries to get a string property form the options object +nsresult +GetStringPropFromOptions(JSContext *cx, HandleObject from, const char *name, nsCString &prop) +{ + RootedValue value(cx); + bool found; + nsresult rv = GetPropFromOptions(cx, from, name, &value, &found); + NS_ENSURE_SUCCESS(rv, rv); + + if (!found) + return NS_OK; + + NS_ENSURE_TRUE(value.isString(), NS_ERROR_INVALID_ARG); + + char *tmp = JS_EncodeString(cx, value.toString()); + NS_ENSURE_TRUE(tmp, NS_ERROR_INVALID_ARG); + prop.Adopt(tmp, strlen(tmp)); + return NS_OK; +} + +// helper that parsing the sandbox options object (from) and sets the fields of the incoming options struct (options) +nsresult +ParseOptionsObject(JSContext *cx, jsval from, SandboxOptions &options) +{ + NS_ENSURE_TRUE(from.isObject(), NS_ERROR_INVALID_ARG); + RootedObject optionsObject(cx, &from.toObject()); + nsresult rv = GetObjPropFromOptions(cx, optionsObject, + "sandboxPrototype", options.proto.address()); + NS_ENSURE_SUCCESS(rv, rv); + + rv = GetBoolPropFromOptions(cx, optionsObject, + "wantXrays", &options.wantXrays); + NS_ENSURE_SUCCESS(rv, rv); + + rv = GetBoolPropFromOptions(cx, optionsObject, + "wantComponents", &options.wantComponents); + NS_ENSURE_SUCCESS(rv, rv); + + rv = GetBoolPropFromOptions(cx, optionsObject, + "wantXHRConstructor", &options.wantXHRConstructor); + NS_ENSURE_SUCCESS(rv, rv); + + rv = GetBoolPropFromOptions(cx, optionsObject, + "wantExportHelpers", &options.wantExportHelpers); + NS_ENSURE_SUCCESS(rv, rv); + + rv = GetStringPropFromOptions(cx, optionsObject, + "sandboxName", options.sandboxName); + NS_ENSURE_SUCCESS(rv, rv); + + rv = GetObjPropFromOptions(cx, optionsObject, + "sameZoneAs", options.sameZoneAs.address()); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +static nsresult +AssembleSandboxMemoryReporterName(JSContext *cx, nsCString &sandboxName) +{ + // Use a default name when the caller did not provide a sandboxName. + if (sandboxName.IsEmpty()) + sandboxName = NS_LITERAL_CSTRING("[anonymous sandbox]"); + + nsXPConnect* xpc = nsXPConnect::XPConnect(); + // Get the xpconnect native call context. + nsAXPCNativeCallContext *cc = nullptr; + xpc->GetCurrentNativeCallContext(&cc); + NS_ENSURE_TRUE(cc, NS_ERROR_INVALID_ARG); + + // Get the current source info from xpc. + nsCOMPtr frame; + xpc->GetCurrentJSStack(getter_AddRefs(frame)); + + // Append the caller's location information. + if (frame) { + nsCString location; + int32_t lineNumber = 0; + frame->GetFilename(getter_Copies(location)); + frame->GetLineNumber(&lineNumber); + + sandboxName.AppendLiteral(" (from: "); + sandboxName.Append(location); + sandboxName.AppendLiteral(":"); + sandboxName.AppendInt(lineNumber); + sandboxName.AppendLiteral(")"); + } + + return NS_OK; +} + +// static +nsresult +nsXPCComponents_utils_Sandbox::CallOrConstruct(nsIXPConnectWrappedNative *wrapper, + JSContext *cx, HandleObject obj, + const CallArgs &args, bool *_retval) +{ + if (args.length() < 1) + return ThrowAndFail(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx, _retval); + + nsresult rv; + + // Make sure to set up principals on the sandbox before initing classes + nsCOMPtr principal; + nsCOMPtr expanded; + nsCOMPtr prinOrSop; + + if (args[0].isString()) { + RootedString str(cx, args[0].toString()); + rv = GetPrincipalFromString(cx, str, getter_AddRefs(principal)); + prinOrSop = principal; + } else if (args[0].isObject()) { + RootedObject obj(cx, &args[0].toObject()); + if (JS_IsArrayObject(cx, obj)) { + rv = GetExpandedPrincipal(cx, obj, getter_AddRefs(expanded)); + prinOrSop = expanded; + } else { + rv = GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop)); + } + } else { + return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); + } + + if (NS_FAILED(rv)) + return ThrowAndFail(rv, cx, _retval); + + SandboxOptions options(cx); + + if (args.length() > 1 && args[1].isObject()) { + if (NS_FAILED(ParseOptionsObject(cx, args[1], options))) + return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); + } + + if (NS_FAILED(AssembleSandboxMemoryReporterName(cx, options.sandboxName))) + return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); + + rv = xpc_CreateSandboxObject(cx, args.rval().address(), prinOrSop, options); + + if (NS_FAILED(rv)) + return ThrowAndFail(rv, cx, _retval); + + *_retval = true; + + return rv; +} + +class ContextHolder : public nsIScriptObjectPrincipal +{ +public: + ContextHolder(JSContext *aOuterCx, HandleObject aSandbox, nsIPrincipal *aPrincipal); + virtual ~ContextHolder(); + + JSContext * GetJSContext() + { + return mJSContext; + } + + nsIPrincipal * GetPrincipal() { return mPrincipal; } + + NS_DECL_ISUPPORTS + +private: + JSContext* mJSContext; + nsCOMPtr mPrincipal; +}; + +NS_IMPL_ISUPPORTS1(ContextHolder, nsIScriptObjectPrincipal) + +ContextHolder::ContextHolder(JSContext *aOuterCx, + HandleObject aSandbox, + nsIPrincipal *aPrincipal) + : mJSContext(JS_NewContext(JS_GetRuntime(aOuterCx), 1024)), + mPrincipal(aPrincipal) +{ + if (mJSContext) { + bool isChrome; + DebugOnly rv = XPCWrapper::GetSecurityManager()-> + IsSystemPrincipal(mPrincipal, &isChrome); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + JS_SetOptions(mJSContext, + JS_GetOptions(mJSContext) | + JSOPTION_DONT_REPORT_UNCAUGHT | + JSOPTION_PRIVATE_IS_NSISUPPORTS); + js::SetDefaultObjectForContext(mJSContext, aSandbox); + JS_SetContextPrivate(mJSContext, this); + } +} + +ContextHolder::~ContextHolder() +{ + if (mJSContext) + JS_DestroyContextNoGC(mJSContext); +} + +/***************************************************************************/ + /* void evalInSandbox(in AString source, in nativeobj sandbox); */ NS_IMETHODIMP nsXPCComponents_Utils::EvalInSandbox(const nsAString& source, @@ -2840,6 +4203,109 @@ nsXPCComponents_Utils::EvalInSandbox(const nsAString& source, return NS_OK; } +nsresult +xpc_EvalInSandbox(JSContext *cx, HandleObject sandboxArg, const nsAString& source, + const char *filename, int32_t lineNo, + JSVersion jsVersion, bool returnStringOnly, MutableHandleValue rval) +{ + JS_AbortIfWrongThread(JS_GetRuntime(cx)); + rval.set(UndefinedValue()); + + bool waiveXray = xpc::WrapperFactory::HasWaiveXrayFlag(sandboxArg); + RootedObject sandbox(cx, js::CheckedUnwrap(sandboxArg)); + if (!sandbox || js::GetObjectJSClass(sandbox) != &SandboxClass) { + return NS_ERROR_INVALID_ARG; + } + + nsIScriptObjectPrincipal *sop = + (nsIScriptObjectPrincipal*)xpc_GetJSPrivate(sandbox); + MOZ_ASSERT(sop, "Invalid sandbox passed"); + nsCOMPtr prin = sop->GetPrincipal(); + NS_ENSURE_TRUE(prin, NS_ERROR_FAILURE); + + nsAutoCString filenameBuf; + if (!filename) { + // Default to the spec of the principal. + nsJSPrincipals::get(prin)->GetScriptLocation(filenameBuf); + filename = filenameBuf.get(); + lineNo = 1; + } + + // We create a separate cx to do the sandbox evaluation. Scope it. + RootedValue v(cx, UndefinedValue()); + RootedValue exn(cx, UndefinedValue()); + bool ok = true; + { + // Make a special cx for the sandbox and push it. + // NB: As soon as the RefPtr goes away, the cx goes away. So declare + // it first so that it disappears last. + nsRefPtr sandcxHolder = new ContextHolder(cx, sandbox, prin); + JSContext *sandcx = sandcxHolder->GetJSContext(); + if (!sandcx) { + JS_ReportError(cx, "Can't prepare context for evalInSandbox"); + return NS_ERROR_OUT_OF_MEMORY; + } + nsCxPusher pusher; + pusher.Push(sandcx); + JSAutoCompartment ac(sandcx, sandbox); + + JS::CompileOptions options(sandcx); + options.setPrincipals(nsJSPrincipals::get(prin)) + .setFileAndLine(filename, lineNo); + if (jsVersion != JSVERSION_DEFAULT) + options.setVersion(jsVersion); + JS::RootedObject rootedSandbox(sandcx, sandbox); + ok = JS::Evaluate(sandcx, rootedSandbox, options, + PromiseFlatString(source).get(), source.Length(), + v.address()); + if (ok && returnStringOnly && !v.isUndefined()) { + JSString *str = JS_ValueToString(sandcx, v); + ok = !!str; + v = ok ? JS::StringValue(str) : JS::UndefinedValue(); + } + + // If the sandbox threw an exception, grab it off the context. + if (JS_GetPendingException(sandcx, exn.address())) { + MOZ_ASSERT(!ok); + JS_ClearPendingException(sandcx); + if (returnStringOnly) { + // The caller asked for strings only, convert the + // exception into a string. + JSString *str = JS_ValueToString(sandcx, exn); + exn = str ? JS::StringValue(str) : JS::UndefinedValue(); + } + } + } + + // + // Alright, we're back on the caller's cx. If an error occured, try to + // wrap and set the exception. Otherwise, wrap the return value. + // + + if (!ok) { + // If we end up without an exception, it was probably due to OOM along + // the way, in which case we thow. Otherwise, wrap it. + if (exn.isUndefined() || !JS_WrapValue(cx, exn.address())) + return NS_ERROR_OUT_OF_MEMORY; + + // Set the exception on our caller's cx. + JS_SetPendingException(cx, exn); + return NS_ERROR_FAILURE; + } + + // Transitively apply Xray waivers if |sb| was waived. + if (waiveXray) { + ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, v.address()); + } else { + ok = JS_WrapValue(cx, v.address()); + } + NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE); + + // Whew! + rval.set(v); + return NS_OK; +} + /* JSObject import (in AUTF8String registryLocation, * [optional] in JSObject targetObj); */ @@ -3098,6 +4564,73 @@ nsXPCComponents_Utils::CreateDateIn(const Value &vobj, int64_t msec, JSContext * return NS_OK; } +bool +NonCloningFunctionForwarder(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0)); + MOZ_ASSERT(v.isObject(), "weird function"); + + JSObject *obj = JS_THIS_OBJECT(cx, vp); + if (!obj) { + return false; + } + return JS_CallFunctionValue(cx, obj, v, args.length(), args.array(), vp); +} + +/* + * Forwards the call to the exported function. Clones all the non reflectors, ignores + * the |this| argument. + */ +bool +CloningFunctionForwarder(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0)); + NS_ASSERTION(v.isObject(), "weird function"); + RootedObject origFunObj(cx, UncheckedUnwrap(&v.toObject())); + { + JSAutoCompartment ac(cx, origFunObj); + // Note: only the arguments are cloned not the |this| or the |callee|. + // Function forwarder does not use those. + for (unsigned i = 0; i < args.length(); i++) { + if (!CloneNonReflectors(cx, args[i])) { + return false; + } + } + + // JS API does not support any JSObject to JSFunction conversion, + // so let's use JS_CallFunctionValue instead. + RootedValue functionVal(cx); + functionVal.setObject(*origFunObj); + + if (!JS_CallFunctionValue(cx, nullptr, functionVal, args.length(), args.array(), vp)) + return false; + } + + // Return value must be wrapped. + return JS_WrapValue(cx, vp); +} + +bool +NewFunctionForwarder(JSContext *cx, HandleId id, HandleObject callable, bool doclone, + MutableHandleValue vp) +{ + JSFunction *fun = js::NewFunctionByIdWithReserved(cx, doclone ? CloningFunctionForwarder : + NonCloningFunctionForwarder, + 0,0, JS::CurrentGlobalOrNull(cx), id); + + if (!fun) + return false; + + JSObject *funobj = JS_GetFunctionObject(fun); + js::SetFunctionNativeReserved(funobj, 0, ObjectValue(*callable)); + vp.setObject(*funobj); + return true; +} + /* void makeObjectPropsNormal(jsval vobj); */ NS_IMETHODIMP nsXPCComponents_Utils::MakeObjectPropsNormal(const Value &vobj, JSContext *cx) @@ -3327,7 +4860,7 @@ nsXPCComponents_Utils::NukeSandbox(const JS::Value &obj, JSContext *cx) JSObject *wrapper = &obj.toObject(); NS_ENSURE_TRUE(IsWrapper(wrapper), NS_ERROR_INVALID_ARG); JSObject *sb = UncheckedUnwrap(wrapper); - NS_ENSURE_TRUE(IsSandbox(sb), NS_ERROR_INVALID_ARG); + NS_ENSURE_TRUE(GetObjectJSClass(sb) == &SandboxClass, NS_ERROR_INVALID_ARG); NukeCrossCompartmentWrappers(cx, AllCompartments(), SingleCompartment(GetObjectCompartment(sb)), NukeWindowReferences); diff --git a/js/xpconnect/src/moz.build b/js/xpconnect/src/moz.build index f67614c3673a..333faf7130fc 100644 --- a/js/xpconnect/src/moz.build +++ b/js/xpconnect/src/moz.build @@ -51,6 +51,5 @@ CPP_SOURCES += [ 'nsCxPusher.cpp', 'nsScriptError.cpp', 'nsXPConnect.cpp', - 'Sandbox.cpp', ] diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 8fa989ab3fa9..9042a47fb210 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -3681,22 +3681,7 @@ xpc_GetSafeJSContext() return XPCJSRuntime::Get()->GetJSContextStack()->GetSafeJSContext(); } -bool -NewFunctionForwarder(JSContext *cx, JS::HandleId id, JS::HandleObject callable, - bool doclone, JS::MutableHandleValue vp); - -nsresult -ThrowAndFail(nsresult errNum, JSContext* cx, bool* retval); - -// Infallible. -already_AddRefed -NewSandboxConstructor(); - -bool -IsSandbox(JSObject *obj); - namespace xpc { - struct SandboxOptions { SandboxOptions(JSContext *cx) : wantXrays(true) From 58aa335563475ea225596cdd7dc3f00df88f8e57 Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Thu, 22 Aug 2013 19:11:47 +1200 Subject: [PATCH 21/54] Bug 907837 - Fix IWYU false positive to unbreak non-SPS build. r=nrc --- gfx/layers/ImageContainer.h | 1 - gfx/layers/Layers.cpp | 1 - gfx/layers/composite/AsyncCompositionManager.h | 1 - gfx/layers/ipc/AsyncPanZoomController.cpp | 1 - gfx/layers/ipc/Axis.cpp | 1 - gfx/layers/ipc/CompositorParent.h | 1 - gfx/layers/opengl/CompositorOGL.h | 1 - gfx/layers/opengl/LayerManagerOGL.cpp | 1 - 8 files changed, 8 deletions(-) diff --git a/gfx/layers/ImageContainer.h b/gfx/layers/ImageContainer.h index e013f691a3ce..b9c6a2806167 100644 --- a/gfx/layers/ImageContainer.h +++ b/gfx/layers/ImageContainer.h @@ -8,7 +8,6 @@ #include // for uint32_t, uint8_t, uint64_t #include // for int32_t -#include "GeckoProfilerFunc.h" // for TimeStamp #include "ImageTypes.h" // for ImageFormat, etc #include "gfxASurface.h" // for gfxASurface, etc #include "gfxPoint.h" // for gfxIntSize diff --git a/gfx/layers/Layers.cpp b/gfx/layers/Layers.cpp index f31e37df5ef9..d7bb2e578968 100644 --- a/gfx/layers/Layers.cpp +++ b/gfx/layers/Layers.cpp @@ -9,7 +9,6 @@ #include // for max, min #include "AnimationCommon.h" // for ComputedTimingFunction #include "CompositableHost.h" // for CompositableHost -#include "GeckoProfilerFunc.h" // for TimeStamp, TimeDuration #include "ImageContainer.h" // for ImageContainer, etc #include "ImageLayers.h" // for ImageLayer #include "LayerSorter.h" // for SortLayersBy3DZOrder diff --git a/gfx/layers/composite/AsyncCompositionManager.h b/gfx/layers/composite/AsyncCompositionManager.h index 9265046741c5..f04cd6714dcf 100644 --- a/gfx/layers/composite/AsyncCompositionManager.h +++ b/gfx/layers/composite/AsyncCompositionManager.h @@ -6,7 +6,6 @@ #ifndef GFX_ASYNCCOMPOSITIONMANAGER_H #define GFX_ASYNCCOMPOSITIONMANAGER_H -#include "GeckoProfilerFunc.h" // for TimeStamp #include "Units.h" // for LayerPoint, etc #include "mozilla/layers/LayerManagerComposite.h" // for LayerManagerComposite #include "gfx3DMatrix.h" // for gfx3DMatrix diff --git a/gfx/layers/ipc/AsyncPanZoomController.cpp b/gfx/layers/ipc/AsyncPanZoomController.cpp index b53cf3294ae8..2ff914cc8387 100644 --- a/gfx/layers/ipc/AsyncPanZoomController.cpp +++ b/gfx/layers/ipc/AsyncPanZoomController.cpp @@ -12,7 +12,6 @@ #include "AsyncPanZoomController.h" // for AsyncPanZoomController, etc #include "CompositorParent.h" // for CompositorParent #include "FrameMetrics.h" // for FrameMetrics, etc -#include "GeckoProfilerFunc.h" // for TimeDuration, TimeStamp #include "GestureEventListener.h" // for GestureEventListener #include "InputData.h" // for MultiTouchInput, etc #include "Units.h" // for CSSRect, CSSPoint, etc diff --git a/gfx/layers/ipc/Axis.cpp b/gfx/layers/ipc/Axis.cpp index b506696dc017..5e099232e6c1 100644 --- a/gfx/layers/ipc/Axis.cpp +++ b/gfx/layers/ipc/Axis.cpp @@ -9,7 +9,6 @@ #include // for max #include "AsyncPanZoomController.h" // for AsyncPanZoomController #include "FrameMetrics.h" // for FrameMetrics -#include "GeckoProfilerFunc.h" // for TimeDuration #include "mozilla/Attributes.h" // for MOZ_FINAL #include "mozilla/Preferences.h" // for Preferences #include "mozilla/gfx/Rect.h" // for RoundedIn diff --git a/gfx/layers/ipc/CompositorParent.h b/gfx/layers/ipc/CompositorParent.h index b2fd1ca0ec50..093783d9932c 100644 --- a/gfx/layers/ipc/CompositorParent.h +++ b/gfx/layers/ipc/CompositorParent.h @@ -16,7 +16,6 @@ //#define COMPOSITOR_PERFORMANCE_WARNING #include // for uint64_t -#include "GeckoProfilerFunc.h" // for TimeStamp #include "Layers.h" // for Layer #include "ShadowLayersManager.h" // for ShadowLayersManager #include "base/basictypes.h" // for DISALLOW_EVIL_CONSTRUCTORS diff --git a/gfx/layers/opengl/CompositorOGL.h b/gfx/layers/opengl/CompositorOGL.h index a92317051566..b897c2836603 100644 --- a/gfx/layers/opengl/CompositorOGL.h +++ b/gfx/layers/opengl/CompositorOGL.h @@ -10,7 +10,6 @@ #include "GLContext.h" // for GLContext #include "GLContextTypes.h" // for GLuint, GLenum, GLint #include "GLDefs.h" // for GLintptr, GLvoid, etc -#include "GeckoProfilerFunc.h" // for TimeStamp #include "LayerManagerOGLProgram.h" // for ShaderProgramOGL, etc #include "Units.h" // for ScreenPoint #include "gfxContext.h" // for gfxContext diff --git a/gfx/layers/opengl/LayerManagerOGL.cpp b/gfx/layers/opengl/LayerManagerOGL.cpp index 7b2551a2eb71..20325c0cb71a 100644 --- a/gfx/layers/opengl/LayerManagerOGL.cpp +++ b/gfx/layers/opengl/LayerManagerOGL.cpp @@ -14,7 +14,6 @@ #include "FPSCounter.h" // for FPSState, FPSCounter #include "GLContext.h" // for GLContext, etc #include "GLContextProvider.h" // for GLContextProvider -#include "GeckoProfilerFunc.h" // for TimeStamp #include "GeckoProfiler.h" // for PROFILER_LABEL #include "ImageLayerOGL.h" // for ImageLayerOGL #include "ImageLayers.h" // for ImageLayer From 21390e1de45b0a395729ca0bc01ff60b38b77827 Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Thu, 22 Aug 2013 19:11:51 +1200 Subject: [PATCH 22/54] Bug 907837 - Don't include SPS headers directly. r=nrc --- gfx/layers/ThebesLayerBuffer.cpp | 2 +- gfx/layers/basic/BasicLayerManager.cpp | 2 +- gfx/layers/client/ClientCanvasLayer.cpp | 2 +- gfx/layers/client/ClientLayerManager.cpp | 2 +- gfx/layers/client/ClientThebesLayer.cpp | 2 +- gfx/layers/client/TiledContentClient.cpp | 2 +- gfx/layers/composite/LayerManagerComposite.cpp | 2 +- gfx/layers/ipc/ShadowLayers.cpp | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/gfx/layers/ThebesLayerBuffer.cpp b/gfx/layers/ThebesLayerBuffer.cpp index 26a034b0f9a1..9468cc774b15 100644 --- a/gfx/layers/ThebesLayerBuffer.cpp +++ b/gfx/layers/ThebesLayerBuffer.cpp @@ -8,7 +8,7 @@ #include // for max #include "BasicImplData.h" // for BasicImplData #include "BasicLayersImpl.h" // for ToData -#include "GeckoProfilerImpl.h" // for PROFILER_LABEL +#include "GeckoProfiler.h" // for PROFILER_LABEL #include "Layers.h" // for ThebesLayer, Layer, etc #include "gfxColor.h" // for gfxRGBA #include "gfxContext.h" // for gfxContext, etc diff --git a/gfx/layers/basic/BasicLayerManager.cpp b/gfx/layers/basic/BasicLayerManager.cpp index c85d7fc7d29a..be506cb545c0 100644 --- a/gfx/layers/basic/BasicLayerManager.cpp +++ b/gfx/layers/basic/BasicLayerManager.cpp @@ -8,7 +8,7 @@ #include // for int32_t #include "BasicContainerLayer.h" // for BasicContainerLayer #include "BasicLayersImpl.h" // for ToData, BasicReadbackLayer, etc -#include "GeckoProfilerImpl.h" // for PROFILER_LABEL +#include "GeckoProfiler.h" // for PROFILER_LABEL #include "ImageContainer.h" // for ImageFactory #include "Layers.h" // for Layer, ContainerLayer, etc #include "ReadbackLayer.h" // for ReadbackLayer diff --git a/gfx/layers/client/ClientCanvasLayer.cpp b/gfx/layers/client/ClientCanvasLayer.cpp index 320d6a20adf3..50377179196d 100644 --- a/gfx/layers/client/ClientCanvasLayer.cpp +++ b/gfx/layers/client/ClientCanvasLayer.cpp @@ -6,7 +6,7 @@ #include "ClientCanvasLayer.h" #include "GLContext.h" // for GLContext #include "GLScreenBuffer.h" // for GLScreenBuffer -#include "GeckoProfilerImpl.h" // for PROFILER_LABEL +#include "GeckoProfiler.h" // for PROFILER_LABEL #include "SharedSurfaceEGL.h" // for SurfaceFactory_EGLImage #include "SharedSurfaceGL.h" // for SurfaceFactory_GLTexture, etc #include "SurfaceStream.h" // for SurfaceStream, etc diff --git a/gfx/layers/client/ClientLayerManager.cpp b/gfx/layers/client/ClientLayerManager.cpp index a8bb57c83168..d8448f50bec0 100644 --- a/gfx/layers/client/ClientLayerManager.cpp +++ b/gfx/layers/client/ClientLayerManager.cpp @@ -5,7 +5,7 @@ #include "ClientLayerManager.h" #include "CompositorChild.h" // for CompositorChild -#include "GeckoProfilerImpl.h" // for PROFILER_LABEL +#include "GeckoProfiler.h" // for PROFILER_LABEL #include "gfx3DMatrix.h" // for gfx3DMatrix #include "gfxASurface.h" // for gfxASurface, etc #include "ipc/AutoOpenSurface.h" // for AutoOpenSurface diff --git a/gfx/layers/client/ClientThebesLayer.cpp b/gfx/layers/client/ClientThebesLayer.cpp index 833cdb475c67..89caa52a2118 100644 --- a/gfx/layers/client/ClientThebesLayer.cpp +++ b/gfx/layers/client/ClientThebesLayer.cpp @@ -6,7 +6,7 @@ #include "ClientThebesLayer.h" #include "ClientTiledThebesLayer.h" // for ClientTiledThebesLayer #include // for uint32_t -#include "GeckoProfilerImpl.h" // for PROFILER_LABEL +#include "GeckoProfiler.h" // for PROFILER_LABEL #include "client/ClientLayerManager.h" // for ClientLayerManager, etc #include "gfxASurface.h" // for gfxASurface, etc #include "gfxContext.h" // for gfxContext diff --git a/gfx/layers/client/TiledContentClient.cpp b/gfx/layers/client/TiledContentClient.cpp index 1f21aa83162b..1d3170ff1048 100644 --- a/gfx/layers/client/TiledContentClient.cpp +++ b/gfx/layers/client/TiledContentClient.cpp @@ -6,7 +6,7 @@ #include "mozilla/layers/TiledContentClient.h" #include // for ceil, ceilf, floor #include "ClientTiledThebesLayer.h" // for ClientTiledThebesLayer -#include "GeckoProfilerImpl.h" // for PROFILER_LABEL +#include "GeckoProfiler.h" // for PROFILER_LABEL #include "ClientLayerManager.h" // for ClientLayerManager #include "gfxContext.h" // for gfxContext, etc #include "gfxPlatform.h" // for gfxPlatform diff --git a/gfx/layers/composite/LayerManagerComposite.cpp b/gfx/layers/composite/LayerManagerComposite.cpp index cac3de38074b..36273e632c8c 100644 --- a/gfx/layers/composite/LayerManagerComposite.cpp +++ b/gfx/layers/composite/LayerManagerComposite.cpp @@ -12,7 +12,7 @@ #include "CompositableHost.h" // for CompositableHost #include "ContainerLayerComposite.h" // for ContainerLayerComposite, etc #include "FrameMetrics.h" // for FrameMetrics -#include "GeckoProfilerImpl.h" // for profiler_set_frame_number, etc +#include "GeckoProfiler.h" // for profiler_set_frame_number, etc #include "ImageLayerComposite.h" // for ImageLayerComposite #include "Layers.h" // for Layer, ContainerLayer, etc #include "ThebesLayerComposite.h" // for ThebesLayerComposite diff --git a/gfx/layers/ipc/ShadowLayers.cpp b/gfx/layers/ipc/ShadowLayers.cpp index 30d563d0d022..8539bc590237 100644 --- a/gfx/layers/ipc/ShadowLayers.cpp +++ b/gfx/layers/ipc/ShadowLayers.cpp @@ -9,7 +9,7 @@ #include // for _Rb_tree_const_iterator, etc #include // for vector #include "AutoOpenSurface.h" // for AutoOpenSurface, etc -#include "GeckoProfilerImpl.h" // for PROFILER_LABEL +#include "GeckoProfiler.h" // for PROFILER_LABEL #include "ISurfaceAllocator.h" // for IsSurfaceDescriptorValid #include "Layers.h" // for Layer #include "RenderTrace.h" // for RenderTraceScope From 5d37448b15a3dceea9f4859f45b091794543091c Mon Sep 17 00:00:00 2001 From: Neil Rashbrook Date: Thu, 22 Aug 2013 08:38:57 +0100 Subject: [PATCH 23/54] Bug 907904 Fix string correctness issues r=bsmith --- security/manager/ssl/src/JARSignatureVerification.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/security/manager/ssl/src/JARSignatureVerification.cpp b/security/manager/ssl/src/JARSignatureVerification.cpp index 100ce13d0774..5996f237ebbf 100644 --- a/security/manager/ssl/src/JARSignatureVerification.cpp +++ b/security/manager/ssl/src/JARSignatureVerification.cpp @@ -254,8 +254,8 @@ ReadLine(/*in/out*/ const char* & nextLineStart, /*out*/ nsCString & line, #define JAR_MF_SEARCH_STRING "(M|/M)ETA-INF/(M|m)(ANIFEST|anifest).(MF|mf)$" #define JAR_SF_SEARCH_STRING "(M|/M)ETA-INF/*.(SF|sf)$" #define JAR_RSA_SEARCH_STRING "(M|/M)ETA-INF/*.(RSA|rsa)$" -#define JAR_MF_HEADER (const char*)"Manifest-Version: 1.0" -#define JAR_SF_HEADER (const char*)"Signature-Version: 1.0" +#define JAR_MF_HEADER "Manifest-Version: 1.0" +#define JAR_SF_HEADER "Signature-Version: 1.0" nsresult ParseAttribute(const nsAutoCString & curLine, @@ -339,7 +339,7 @@ ParseSF(const char* filebuf, /*out*/ SECItem & mfDigest) nsresult rv; const char* nextLineStart = filebuf; - rv = CheckManifestVersion(nextLineStart, nsLiteralCString(JAR_SF_HEADER)); + rv = CheckManifestVersion(nextLineStart, NS_LITERAL_CSTRING(JAR_SF_HEADER)); if (NS_FAILED(rv)) return rv; @@ -399,7 +399,7 @@ ParseMF(const char* filebuf, nsIZipReader * zip, const char* nextLineStart = filebuf; - rv = CheckManifestVersion(nextLineStart, nsLiteralCString(JAR_MF_HEADER)); + rv = CheckManifestVersion(nextLineStart, NS_LITERAL_CSTRING(JAR_MF_HEADER)); if (NS_FAILED(rv)) { return rv; } @@ -642,7 +642,7 @@ OpenSignedJARFile(nsIFile * aJarFile, // Verify every entry in the file. nsCOMPtr entries; - rv = zip->FindEntries(NS_LITERAL_CSTRING(""), getter_AddRefs(entries)); + rv = zip->FindEntries(EmptyCString(), getter_AddRefs(entries)); if (NS_SUCCEEDED(rv) && !entries) { rv = NS_ERROR_UNEXPECTED; } From b62bcfa0e32feea3eefeb3ab761304d312b881d9 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 19 Aug 2013 23:43:47 -0700 Subject: [PATCH 24/54] Bug 905017 (part 2) - Move structured clone stuff from jsapi.{h,cpp} and jsclone.{h,cpp} to js/StructuredClone.{h,cpp}. r=billm. --HG-- rename : js/src/jsclone.cpp => js/src/vm/StructuredClone.cpp extra : rebase_source : ecce9699ddb45535aa94d5e3add901f5a8620c46 --- config/check_spidermonkey_style.py | 3 +- dom/base/StructuredCloneTags.h | 2 + dom/base/nsStructuredCloneContainer.cpp | 1 + dom/bluetooth/BluetoothReplyRunnable.h | 2 +- dom/indexedDB/IndexedDatabase.h | 2 +- dom/indexedDB/Key.h | 10 +- dom/ipc/StructuredCloneUtils.cpp | 1 + dom/ipc/StructuredCloneUtils.h | 2 +- dom/workers/Events.h | 2 +- dom/workers/XMLHttpRequest.h | 2 + ipc/glue/IPCMessageUtils.h | 2 +- js/public/StructuredClone.h | 162 ++++++ js/public/Value.h | 11 + js/src/config/check_spidermonkey_style.py | 3 +- js/src/jsapi.cpp | 230 +------- js/src/jsapi.h | 180 +------ js/src/jsclone.h | 206 -------- js/src/moz.build | 3 +- js/src/shell/js.cpp | 1 + .../{jsclone.cpp => vm/StructuredClone.cpp} | 489 ++++++++++++++++-- 20 files changed, 655 insertions(+), 659 deletions(-) create mode 100644 js/public/StructuredClone.h delete mode 100644 js/src/jsclone.h rename js/src/{jsclone.cpp => vm/StructuredClone.cpp} (75%) diff --git a/config/check_spidermonkey_style.py b/config/check_spidermonkey_style.py index 93e9e8a68877..c7e8d2934c6f 100644 --- a/config/check_spidermonkey_style.py +++ b/config/check_spidermonkey_style.py @@ -337,7 +337,8 @@ class Include(object): # handling. if module == module_name(self.inclname) or \ module == 'jsmemorymetrics' and self.inclname == 'js/MemoryMetrics.h' or \ - module == 'vm/PropertyKey' and self.inclname == 'js/PropertyKey.h': + module == 'vm/PropertyKey' and self.inclname == 'js/PropertyKey.h' or \ + module == 'vm/StructuredClone' and self.inclname == 'js/StructuredClone.h': return 0 if '/' in self.inclname: diff --git a/dom/base/StructuredCloneTags.h b/dom/base/StructuredCloneTags.h index 4246b46ea139..aa3781b2a3e6 100644 --- a/dom/base/StructuredCloneTags.h +++ b/dom/base/StructuredCloneTags.h @@ -5,6 +5,8 @@ #ifndef StructuredCloneTags_h__ #define StructuredCloneTags_h__ +#include "js/StructuredClone.h" + namespace mozilla { namespace dom { diff --git a/dom/base/nsStructuredCloneContainer.cpp b/dom/base/nsStructuredCloneContainer.cpp index 9f9bee55f309..8cacc5928031 100644 --- a/dom/base/nsStructuredCloneContainer.cpp +++ b/dom/base/nsStructuredCloneContainer.cpp @@ -14,6 +14,7 @@ #include "nsServiceManagerUtils.h" #include "nsContentUtils.h" #include "jsapi.h" +#include "js/StructuredClone.h" #include "mozilla/Base64.h" diff --git a/dom/bluetooth/BluetoothReplyRunnable.h b/dom/bluetooth/BluetoothReplyRunnable.h index 8124ba94005b..e700b5fbb069 100644 --- a/dom/bluetooth/BluetoothReplyRunnable.h +++ b/dom/bluetooth/BluetoothReplyRunnable.h @@ -10,7 +10,7 @@ #include "mozilla/Attributes.h" #include "BluetoothCommon.h" #include "nsThreadUtils.h" -#include "jsapi.h" +#include "js/Value.h" class nsIDOMDOMRequest; diff --git a/dom/indexedDB/IndexedDatabase.h b/dom/indexedDB/IndexedDatabase.h index b0ea8965f6c9..582dfc34c390 100644 --- a/dom/indexedDB/IndexedDatabase.h +++ b/dom/indexedDB/IndexedDatabase.h @@ -10,7 +10,7 @@ #include "nsIProgrammingLanguage.h" #include "mozilla/Attributes.h" -#include "jsapi.h" +#include "js/StructuredClone.h" #include "nsAutoPtr.h" #include "nsCOMPtr.h" #include "nsDebug.h" diff --git a/dom/indexedDB/Key.h b/dom/indexedDB/Key.h index 2ce40d03497b..8b4932193b10 100644 --- a/dom/indexedDB/Key.h +++ b/dom/indexedDB/Key.h @@ -11,6 +11,8 @@ #include "mozIStorageStatement.h" +#include "js/Value.h" + namespace IPC { template struct ParamTraits; } // namespace IPC @@ -159,7 +161,7 @@ public: } nsresult SetFromJSVal(JSContext* aCx, - const jsval aVal) + const JS::Value aVal) { mBuffer.Truncate(); @@ -209,7 +211,7 @@ public: nsresult AppendItem(JSContext* aCx, bool aFirstOfArray, - const jsval aVal) + const JS::Value aVal) { nsresult rv = EncodeJSVal(aCx, aVal, aFirstOfArray ? eMaxType : 0); if (NS_FAILED(rv)) { @@ -303,7 +305,7 @@ private: } // Encoding functions. These append the encoded value to the end of mBuffer - inline nsresult EncodeJSVal(JSContext* aCx, const jsval aVal, + inline nsresult EncodeJSVal(JSContext* aCx, const JS::Value aVal, uint8_t aTypeOffset) { return EncodeJSValInternal(aCx, aVal, aTypeOffset, 0); @@ -329,7 +331,7 @@ private: nsCString mBuffer; private: - nsresult EncodeJSValInternal(JSContext* aCx, const jsval aVal, + nsresult EncodeJSValInternal(JSContext* aCx, const JS::Value aVal, uint8_t aTypeOffset, uint16_t aRecursionDepth); static nsresult DecodeJSValInternal(const unsigned char*& aPos, diff --git a/dom/ipc/StructuredCloneUtils.cpp b/dom/ipc/StructuredCloneUtils.cpp index 39c0507b184e..00f64235d936 100644 --- a/dom/ipc/StructuredCloneUtils.cpp +++ b/dom/ipc/StructuredCloneUtils.cpp @@ -15,6 +15,7 @@ #include "nsJSEnvironment.h" #include "nsThreadUtils.h" #include "StructuredCloneTags.h" +#include "jsapi.h" using namespace mozilla::dom; diff --git a/dom/ipc/StructuredCloneUtils.h b/dom/ipc/StructuredCloneUtils.h index 9c285775b2db..d403558661c9 100644 --- a/dom/ipc/StructuredCloneUtils.h +++ b/dom/ipc/StructuredCloneUtils.h @@ -11,7 +11,7 @@ #include "nsTArray.h" #include "nsIDOMFile.h" -#include "jsapi.h" +#include "js/StructuredClone.h" namespace mozilla { diff --git a/dom/workers/Events.h b/dom/workers/Events.h index cbd252306677..16575e56f5d4 100644 --- a/dom/workers/Events.h +++ b/dom/workers/Events.h @@ -8,7 +8,7 @@ #include "Workers.h" -class JSAutoStructuredCloneBuffer; +#include "js/StructuredClone.h" BEGIN_WORKERS_NAMESPACE diff --git a/dom/workers/XMLHttpRequest.h b/dom/workers/XMLHttpRequest.h index 458813570ecc..24239eacbb48 100644 --- a/dom/workers/XMLHttpRequest.h +++ b/dom/workers/XMLHttpRequest.h @@ -15,6 +15,8 @@ #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/TypedArray.h" +#include "js/StructuredClone.h" + BEGIN_WORKERS_NAMESPACE class Proxy; diff --git a/ipc/glue/IPCMessageUtils.h b/ipc/glue/IPCMessageUtils.h index bbc1fb661160..83e267b25ed2 100644 --- a/ipc/glue/IPCMessageUtils.h +++ b/ipc/glue/IPCMessageUtils.h @@ -22,7 +22,7 @@ #include "nsMemory.h" #include "nsStringGlue.h" #include "nsTArray.h" -#include "jsapi.h" +#include "js/StructuredClone.h" #include "nsCSSProperty.h" #ifdef _MSC_VER diff --git a/js/public/StructuredClone.h b/js/public/StructuredClone.h new file mode 100644 index 000000000000..22084596e732 --- /dev/null +++ b/js/public/StructuredClone.h @@ -0,0 +1,162 @@ +/* -*- 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 js_StructuredClone_h +#define js_StructuredClone_h + +#include + +#include "jstypes.h" + +struct JSContext; +class JSObject; +struct JSRuntime; +struct JSStructuredCloneReader; +struct JSStructuredCloneWriter; + +namespace JS { +template class Handle; +class Value; +} + +// API for the HTML5 internal structured cloning algorithm. + +// Read structured data from the reader r. This hook is used to read a value +// previously serialized by a call to the WriteStructuredCloneOp hook. +// +// tag and data are the pair of uint32_t values from the header. The callback +// may use the JS_Read* APIs to read any other relevant parts of the object +// from the reader r. closure is any value passed to the JS_ReadStructuredClone +// function. Return the new object on success, NULL on error/exception. +typedef JSObject *(*ReadStructuredCloneOp)(JSContext *cx, JSStructuredCloneReader *r, + uint32_t tag, uint32_t data, void *closure); + +// Structured data serialization hook. The engine can write primitive values, +// Objects, Arrays, Dates, RegExps, TypedArrays, and ArrayBuffers. Any other +// type of object requires application support. This callback must first use +// the JS_WriteUint32Pair API to write an object header, passing a value +// greater than JS_SCTAG_USER to the tag parameter. Then it can use the +// JS_Write* APIs to write any other relevant parts of the value v to the +// writer w. closure is any value passed to the JS_WriteStructuredCLone function. +// +// Return true on success, false on error/exception. +typedef bool (*WriteStructuredCloneOp)(JSContext *cx, JSStructuredCloneWriter *w, + JS::Handle obj, void *closure); + +// This is called when JS_WriteStructuredClone is given an invalid transferable. +// To follow HTML5, the application must throw a DATA_CLONE_ERR DOMException +// with error set to one of the JS_SCERR_* values. +typedef void (*StructuredCloneErrorOp)(JSContext *cx, uint32_t errorid); + +// The maximum supported structured-clone serialization format version. +#define JS_STRUCTURED_CLONE_VERSION 2 + +struct JSStructuredCloneCallbacks { + ReadStructuredCloneOp read; + WriteStructuredCloneOp write; + StructuredCloneErrorOp reportError; +}; + +// Note: if the *data contains transferable objects, it can be read only once. +JS_PUBLIC_API(bool) +JS_ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, uint32_t version, + JS::Value *vp, const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure); + +// Note: On success, the caller is responsible for calling +// JS_ClearStructuredClone(*datap, nbytesp). +JS_PUBLIC_API(bool) +JS_WriteStructuredClone(JSContext *cx, JS::Value v, uint64_t **datap, size_t *nbytesp, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure, JS::Value transferable); + +JS_PUBLIC_API(bool) +JS_ClearStructuredClone(const uint64_t *data, size_t nbytes); + +JS_PUBLIC_API(bool) +JS_StructuredCloneHasTransferables(const uint64_t *data, size_t nbytes, bool *hasTransferable); + +JS_PUBLIC_API(bool) +JS_StructuredClone(JSContext *cx, JS::Value v, JS::Value *vp, + const JSStructuredCloneCallbacks *optionalCallbacks, void *closure); + +// RAII sugar for JS_WriteStructuredClone. +class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) { + uint64_t *data_; + size_t nbytes_; + uint32_t version_; + + public: + JSAutoStructuredCloneBuffer() + : data_(NULL), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION) {} + + ~JSAutoStructuredCloneBuffer() { clear(); } + + uint64_t *data() const { return data_; } + size_t nbytes() const { return nbytes_; } + + void clear(); + + // Copy some memory. It will be automatically freed by the destructor. + bool copy(const uint64_t *data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION); + + // Adopt some memory. It will be automatically freed by the destructor. + // data must have been allocated by the JS engine (e.g., extracted via + // JSAutoStructuredCloneBuffer::steal). + void adopt(uint64_t *data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION); + + // Remove the buffer so that it will not be automatically freed. + // After this, the caller is responsible for feeding the memory back to + // JSAutoStructuredCloneBuffer::adopt. + void steal(uint64_t **datap, size_t *nbytesp, uint32_t *versionp=NULL); + + bool read(JSContext *cx, JS::Value *vp, + const JSStructuredCloneCallbacks *optionalCallbacks=NULL, void *closure=NULL); + + bool write(JSContext *cx, JS::Value v, + const JSStructuredCloneCallbacks *optionalCallbacks=NULL, void *closure=NULL); + + bool write(JSContext *cx, JS::Value v, JS::Value transferable, + const JSStructuredCloneCallbacks *optionalCallbacks=NULL, void *closure=NULL); + + // Swap ownership with another JSAutoStructuredCloneBuffer. + void swap(JSAutoStructuredCloneBuffer &other); + + private: + // Copy and assignment are not supported. + JSAutoStructuredCloneBuffer(const JSAutoStructuredCloneBuffer &other); + JSAutoStructuredCloneBuffer &operator=(const JSAutoStructuredCloneBuffer &other); +}; + +// The range of tag values the application may use for its own custom object types. +#define JS_SCTAG_USER_MIN ((uint32_t) 0xFFFF8000) +#define JS_SCTAG_USER_MAX ((uint32_t) 0xFFFFFFFF) + +#define JS_SCERR_RECURSION 0 +#define JS_SCERR_TRANSFERABLE 1 + +JS_PUBLIC_API(void) +JS_SetStructuredCloneCallbacks(JSRuntime *rt, const JSStructuredCloneCallbacks *callbacks); + +JS_PUBLIC_API(bool) +JS_ReadUint32Pair(JSStructuredCloneReader *r, uint32_t *p1, uint32_t *p2); + +JS_PUBLIC_API(bool) +JS_ReadBytes(JSStructuredCloneReader *r, void *p, size_t len); + +JS_PUBLIC_API(bool) +JS_ReadTypedArray(JSStructuredCloneReader *r, JS::Value *vp); + +JS_PUBLIC_API(bool) +JS_WriteUint32Pair(JSStructuredCloneWriter *w, uint32_t tag, uint32_t data); + +JS_PUBLIC_API(bool) +JS_WriteBytes(JSStructuredCloneWriter *w, const void *p, size_t len); + +JS_PUBLIC_API(bool) +JS_WriteTypedArray(JSStructuredCloneWriter *w, JS::Value v); + +#endif /* js_StructuredClone_h */ diff --git a/js/public/Value.h b/js/public/Value.h index e62ea8055912..394c25e09e8c 100644 --- a/js/public/Value.h +++ b/js/public/Value.h @@ -1896,6 +1896,17 @@ JSVAL_TO_PRIVATE(jsval v) return JSVAL_TO_PRIVATE_PTR_IMPL(JSVAL_TO_IMPL(v)); } +// JS constants. For efficiency, prefer predicates (e.g. v.isNull()) and +// constructing values from scratch (e.g. Int32Value(0)). These constants are +// stored in memory and initialized at startup, so testing against them and +// using them requires memory loads and will be correspondingly slow. +extern JS_PUBLIC_DATA(const jsval) JSVAL_NULL; +extern JS_PUBLIC_DATA(const jsval) JSVAL_ZERO; +extern JS_PUBLIC_DATA(const jsval) JSVAL_ONE; +extern JS_PUBLIC_DATA(const jsval) JSVAL_FALSE; +extern JS_PUBLIC_DATA(const jsval) JSVAL_TRUE; +extern JS_PUBLIC_DATA(const jsval) JSVAL_VOID; + #undef JS_VALUE_IS_CONSTEXPR #undef JS_RETURN_LAYOUT_FROM_BITS diff --git a/js/src/config/check_spidermonkey_style.py b/js/src/config/check_spidermonkey_style.py index 93e9e8a68877..c7e8d2934c6f 100644 --- a/js/src/config/check_spidermonkey_style.py +++ b/js/src/config/check_spidermonkey_style.py @@ -337,7 +337,8 @@ class Include(object): # handling. if module == module_name(self.inclname) or \ module == 'jsmemorymetrics' and self.inclname == 'js/MemoryMetrics.h' or \ - module == 'vm/PropertyKey' and self.inclname == 'js/PropertyKey.h': + module == 'vm/PropertyKey' and self.inclname == 'js/PropertyKey.h' or \ + module == 'vm/StructuredClone' and self.inclname == 'js/StructuredClone.h': return 0 if '/' in self.inclname: diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index e667fdfe90cf..96c97e6de95f 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -21,7 +21,6 @@ #include "jsarray.h" #include "jsatom.h" #include "jsbool.h" -#include "jsclone.h" #include "jscntxt.h" #include "jsdate.h" #include "jsexn.h" @@ -60,6 +59,7 @@ #include "gc/Marking.h" #include "jit/AsmJSLink.h" #include "js/CharacterEncoding.h" +#include "js/StructuredClone.h" #if ENABLE_INTL_API #include "unicode/uclean.h" #include "unicode/utypes.h" @@ -134,10 +134,8 @@ const jsval JSVAL_ONE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_INT32, 1)); const jsval JSVAL_FALSE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_BOOLEAN, false)); const jsval JSVAL_TRUE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_BOOLEAN, true)); const jsval JSVAL_VOID = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_UNDEFINED, 0)); -const HandleValue JS::NullHandleValue = - HandleValue::fromMarkedLocation(&JSVAL_NULL); -const HandleValue JS::UndefinedHandleValue = - HandleValue::fromMarkedLocation(&JSVAL_VOID); +const HandleValue JS::NullHandleValue = HandleValue::fromMarkedLocation(&JSVAL_NULL); +const HandleValue JS::UndefinedHandleValue = HandleValue::fromMarkedLocation(&JSVAL_VOID); const jsid voidIdValue = JSID_VOID; const jsid emptyIdValue = JSID_EMPTY; @@ -185,18 +183,22 @@ JS_GetEmptyString(JSRuntime *rt) return rt->emptyString; } -static void +namespace js { + +void AssertHeapIsIdle(JSRuntime *rt) { JS_ASSERT(rt->heapState == js::Idle); } -static void +void AssertHeapIsIdle(JSContext *cx) { AssertHeapIsIdle(cx->runtime()); } +} + static void AssertHeapIsIdleOrIterating(JSRuntime *rt) { @@ -5925,220 +5927,6 @@ JS_ParseJSONWithReviver(JSContext *cx, const jschar *chars, uint32_t len, jsval return true; } -JS_PUBLIC_API(bool) -JS_ReadStructuredClone(JSContext *cx, uint64_t *buf, size_t nbytes, - uint32_t version, jsval *vp, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure) -{ - AssertHeapIsIdle(cx); - CHECK_REQUEST(cx); - - if (version > JS_STRUCTURED_CLONE_VERSION) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CLONE_VERSION); - return false; - } - const JSStructuredCloneCallbacks *callbacks = - optionalCallbacks ? - optionalCallbacks : - cx->runtime()->structuredCloneCallbacks; - return ReadStructuredClone(cx, buf, nbytes, vp, callbacks, closure); -} - -JS_PUBLIC_API(bool) -JS_WriteStructuredClone(JSContext *cx, jsval valueArg, uint64_t **bufp, size_t *nbytesp, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure, jsval transferable) -{ - RootedValue value(cx, valueArg); - AssertHeapIsIdle(cx); - CHECK_REQUEST(cx); - assertSameCompartment(cx, value); - - const JSStructuredCloneCallbacks *callbacks = - optionalCallbacks ? - optionalCallbacks : - cx->runtime()->structuredCloneCallbacks; - return WriteStructuredClone(cx, value, (uint64_t **) bufp, nbytesp, - callbacks, closure, transferable); -} - -JS_PUBLIC_API(bool) -JS_ClearStructuredClone(const uint64_t *data, size_t nbytes) -{ - return ClearStructuredClone(data, nbytes); -} - -JS_PUBLIC_API(bool) -JS_StructuredCloneHasTransferables(const uint64_t *data, size_t nbytes, - bool *hasTransferable) -{ - bool transferable; - if (!StructuredCloneHasTransferObjects(data, nbytes, &transferable)) - return false; - - *hasTransferable = transferable; - return true; -} - -JS_PUBLIC_API(bool) -JS_StructuredClone(JSContext *cx, jsval valueArg, jsval *vp, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure) -{ - RootedValue value(cx, valueArg); - AssertHeapIsIdle(cx); - CHECK_REQUEST(cx); - assertSameCompartment(cx, value); - - const JSStructuredCloneCallbacks *callbacks = - optionalCallbacks ? - optionalCallbacks : - cx->runtime()->structuredCloneCallbacks; - JSAutoStructuredCloneBuffer buf; - return buf.write(cx, value, callbacks, closure) && - buf.read(cx, vp, callbacks, closure); -} - -void -JSAutoStructuredCloneBuffer::clear() -{ - if (data_) { - ClearStructuredClone(data_, nbytes_); - data_ = NULL; - nbytes_ = 0; - version_ = 0; - } -} - -void -JSAutoStructuredCloneBuffer::adopt(uint64_t *data, size_t nbytes, uint32_t version) -{ - clear(); - data_ = data; - nbytes_ = nbytes; - version_ = version; -} - -bool -JSAutoStructuredCloneBuffer::copy(const uint64_t *srcData, size_t nbytes, uint32_t version) -{ - // transferable objects cannot be copied - bool hasTransferable; - if (!StructuredCloneHasTransferObjects(data_, nbytes_, &hasTransferable) || - hasTransferable) - return false; - - uint64_t *newData = static_cast(js_malloc(nbytes)); - if (!newData) - return false; - - js_memcpy(newData, srcData, nbytes); - - clear(); - data_ = newData; - nbytes_ = nbytes; - version_ = version; - return true; -} -void -JSAutoStructuredCloneBuffer::steal(uint64_t **datap, size_t *nbytesp, uint32_t *versionp) -{ - *datap = data_; - *nbytesp = nbytes_; - if (versionp) - *versionp = version_; - - data_ = NULL; - nbytes_ = 0; - version_ = 0; -} - -bool -JSAutoStructuredCloneBuffer::read(JSContext *cx, jsval *vp, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure) -{ - JS_ASSERT(cx); - JS_ASSERT(data_); - return !!JS_ReadStructuredClone(cx, data_, nbytes_, version_, vp, - optionalCallbacks, closure); -} - -bool -JSAutoStructuredCloneBuffer::write(JSContext *cx, jsval valueArg, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure) -{ - jsval transferable = JSVAL_VOID; - return write(cx, valueArg, transferable, optionalCallbacks, closure); -} - -bool -JSAutoStructuredCloneBuffer::write(JSContext *cx, jsval valueArg, - jsval transferable, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure) -{ - RootedValue value(cx, valueArg); - clear(); - bool ok = !!JS_WriteStructuredClone(cx, value, &data_, &nbytes_, - optionalCallbacks, closure, - transferable); - if (!ok) { - data_ = NULL; - nbytes_ = 0; - version_ = JS_STRUCTURED_CLONE_VERSION; - } - return ok; -} - -void -JSAutoStructuredCloneBuffer::swap(JSAutoStructuredCloneBuffer &other) -{ - uint64_t *data = other.data_; - size_t nbytes = other.nbytes_; - uint32_t version = other.version_; - - other.data_ = this->data_; - other.nbytes_ = this->nbytes_; - other.version_ = this->version_; - - this->data_ = data; - this->nbytes_ = nbytes; - this->version_ = version; -} - -JS_PUBLIC_API(void) -JS_SetStructuredCloneCallbacks(JSRuntime *rt, const JSStructuredCloneCallbacks *callbacks) -{ - rt->structuredCloneCallbacks = callbacks; -} - -JS_PUBLIC_API(bool) -JS_ReadUint32Pair(JSStructuredCloneReader *r, uint32_t *p1, uint32_t *p2) -{ - return r->input().readPair((uint32_t *) p1, (uint32_t *) p2); -} - -JS_PUBLIC_API(bool) -JS_ReadBytes(JSStructuredCloneReader *r, void *p, size_t len) -{ - return r->input().readBytes(p, len); -} - -JS_PUBLIC_API(bool) -JS_WriteUint32Pair(JSStructuredCloneWriter *w, uint32_t tag, uint32_t data) -{ - return w->output().writePair(tag, data); -} - -JS_PUBLIC_API(bool) -JS_WriteBytes(JSStructuredCloneWriter *w, const void *p, size_t len) -{ - return w->output().writeBytes(p, len); -} - /************************************************************************/ JS_PUBLIC_API(void) diff --git a/js/src/jsapi.h b/js/src/jsapi.h index f1c9f61c7327..b27ee2c39621 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1147,54 +1147,8 @@ typedef void (* JSCompartmentNameCallback)(JSRuntime *rt, JSCompartment *compartment, char *buf, size_t bufsize); -/* - * Read structured data from the reader r. This hook is used to read a value - * previously serialized by a call to the WriteStructuredCloneOp hook. - * - * tag and data are the pair of uint32_t values from the header. The callback - * may use the JS_Read* APIs to read any other relevant parts of the object - * from the reader r. closure is any value passed to the JS_ReadStructuredClone - * function. Return the new object on success, NULL on error/exception. - */ -typedef JSObject *(*ReadStructuredCloneOp)(JSContext *cx, JSStructuredCloneReader *r, - uint32_t tag, uint32_t data, void *closure); - -/* - * Structured data serialization hook. The engine can write primitive values, - * Objects, Arrays, Dates, RegExps, TypedArrays, and ArrayBuffers. Any other - * type of object requires application support. This callback must first use - * the JS_WriteUint32Pair API to write an object header, passing a value - * greater than JS_SCTAG_USER to the tag parameter. Then it can use the - * JS_Write* APIs to write any other relevant parts of the value v to the - * writer w. closure is any value passed to the JS_WriteStructuredCLone function. - * - * Return true on success, false on error/exception. - */ -typedef bool (*WriteStructuredCloneOp)(JSContext *cx, JSStructuredCloneWriter *w, - JS::Handle obj, void *closure); - -/* - * This is called when JS_WriteStructuredClone is given an invalid transferable. - * To follow HTML5, the application must throw a DATA_CLONE_ERR DOMException - * with error set to one of the JS_SCERR_* values. - */ -typedef void (*StructuredCloneErrorOp)(JSContext *cx, uint32_t errorid); - /************************************************************************/ -/* - * JS constants. For efficiency, prefer predicates (e.g. v.isNull()) and - * constructing values from scratch (e.g. Int32Value(0)). These constants are - * stored in memory and initialized at startup, so testing against them and - * using them requires memory loads and will be correspondingly slow. - */ -extern JS_PUBLIC_DATA(const jsval) JSVAL_NULL; -extern JS_PUBLIC_DATA(const jsval) JSVAL_ZERO; -extern JS_PUBLIC_DATA(const jsval) JSVAL_ONE; -extern JS_PUBLIC_DATA(const jsval) JSVAL_FALSE; -extern JS_PUBLIC_DATA(const jsval) JSVAL_TRUE; -extern JS_PUBLIC_DATA(const jsval) JSVAL_VOID; - static JS_ALWAYS_INLINE jsval JS_NumberValue(double d) { @@ -1876,6 +1830,12 @@ template <> struct GCMethods #endif }; +void +AssertHeapIsIdle(JSRuntime *rt); + +void +AssertHeapIsIdle(JSContext *cx); + } /* namespace js */ class JSAutoRequest @@ -4709,134 +4669,6 @@ JS_ParseJSONWithReviver(JSContext *cx, const jschar *chars, uint32_t len, jsval /************************************************************************/ -/* API for the HTML5 internal structured cloning algorithm. */ - -/* The maximum supported structured-clone serialization format version. */ -#define JS_STRUCTURED_CLONE_VERSION 2 - -struct JSStructuredCloneCallbacks { - ReadStructuredCloneOp read; - WriteStructuredCloneOp write; - StructuredCloneErrorOp reportError; -}; - -/* Note: if the *data contains transferable objects, it can be read - * only once */ -JS_PUBLIC_API(bool) -JS_ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, - uint32_t version, jsval *vp, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure); - -/* Note: On success, the caller is responsible for calling - * JS_ClearStructuredClone(*datap, nbytesp). */ -JS_PUBLIC_API(bool) -JS_WriteStructuredClone(JSContext *cx, jsval v, uint64_t **datap, size_t *nbytesp, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure, jsval transferable); - -JS_PUBLIC_API(bool) -JS_ClearStructuredClone(const uint64_t *data, size_t nbytes); - -JS_PUBLIC_API(bool) -JS_StructuredCloneHasTransferables(const uint64_t *data, size_t nbytes, - bool *hasTransferable); - -JS_PUBLIC_API(bool) -JS_StructuredClone(JSContext *cx, jsval v, jsval *vp, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure); - -/* RAII sugar for JS_WriteStructuredClone. */ -class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) { - uint64_t *data_; - size_t nbytes_; - uint32_t version_; - - public: - JSAutoStructuredCloneBuffer() - : data_(NULL), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION) {} - - ~JSAutoStructuredCloneBuffer() { clear(); } - - uint64_t *data() const { return data_; } - size_t nbytes() const { return nbytes_; } - - void clear(); - - /* Copy some memory. It will be automatically freed by the destructor. */ - bool copy(const uint64_t *data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION); - - /* - * Adopt some memory. It will be automatically freed by the destructor. - * data must have been allocated by the JS engine (e.g., extracted via - * JSAutoStructuredCloneBuffer::steal). - */ - void adopt(uint64_t *data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION); - - /* - * Remove the buffer so that it will not be automatically freed. - * After this, the caller is responsible for feeding the memory back to - * JSAutoStructuredCloneBuffer::adopt. - */ - void steal(uint64_t **datap, size_t *nbytesp, uint32_t *versionp=NULL); - - bool read(JSContext *cx, jsval *vp, - const JSStructuredCloneCallbacks *optionalCallbacks=NULL, - void *closure=NULL); - - bool write(JSContext *cx, jsval v, - const JSStructuredCloneCallbacks *optionalCallbacks=NULL, - void *closure=NULL); - - bool write(JSContext *cx, jsval v, - jsval transferable, - const JSStructuredCloneCallbacks *optionalCallbacks=NULL, - void *closure=NULL); - - /** - * Swap ownership with another JSAutoStructuredCloneBuffer. - */ - void swap(JSAutoStructuredCloneBuffer &other); - - private: - /* Copy and assignment are not supported. */ - JSAutoStructuredCloneBuffer(const JSAutoStructuredCloneBuffer &other); - JSAutoStructuredCloneBuffer &operator=(const JSAutoStructuredCloneBuffer &other); -}; - -/* API for implementing custom serialization behavior (for ImageData, File, etc.) */ - -/* The range of tag values the application may use for its own custom object types. */ -#define JS_SCTAG_USER_MIN ((uint32_t) 0xFFFF8000) -#define JS_SCTAG_USER_MAX ((uint32_t) 0xFFFFFFFF) - -#define JS_SCERR_RECURSION 0 -#define JS_SCERR_TRANSFERABLE 1 - -JS_PUBLIC_API(void) -JS_SetStructuredCloneCallbacks(JSRuntime *rt, const JSStructuredCloneCallbacks *callbacks); - -JS_PUBLIC_API(bool) -JS_ReadUint32Pair(JSStructuredCloneReader *r, uint32_t *p1, uint32_t *p2); - -JS_PUBLIC_API(bool) -JS_ReadBytes(JSStructuredCloneReader *r, void *p, size_t len); - -JS_PUBLIC_API(bool) -JS_ReadTypedArray(JSStructuredCloneReader *r, jsval *vp); - -JS_PUBLIC_API(bool) -JS_WriteUint32Pair(JSStructuredCloneWriter *w, uint32_t tag, uint32_t data); - -JS_PUBLIC_API(bool) -JS_WriteBytes(JSStructuredCloneWriter *w, const void *p, size_t len); - -JS_PUBLIC_API(bool) -JS_WriteTypedArray(JSStructuredCloneWriter *w, jsval v); - -/************************************************************************/ - /* * The default locale for the ECMAScript Internationalization API * (Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat). diff --git a/js/src/jsclone.h b/js/src/jsclone.h deleted file mode 100644 index daec0e86bfe6..000000000000 --- a/js/src/jsclone.h +++ /dev/null @@ -1,206 +0,0 @@ -/* -*- 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 jsclone_h -#define jsclone_h - -#include "jsapi.h" -#include "jscntxt.h" - -#include "js/Vector.h" - -namespace js { - -bool -WriteStructuredClone(JSContext *cx, HandleValue v, uint64_t **bufp, size_t *nbytesp, - const JSStructuredCloneCallbacks *cb, void *cbClosure, - jsval transferable); - -bool -ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, Value *vp, - const JSStructuredCloneCallbacks *cb, void *cbClosure); - -bool -ClearStructuredClone(const uint64_t *data, size_t nbytes); - -bool -StructuredCloneHasTransferObjects(const uint64_t *data, size_t nbytes, - bool *hasTransferable); - -struct SCOutput { - public: - explicit SCOutput(JSContext *cx); - - JSContext *context() const { return cx; } - - bool write(uint64_t u); - bool writePair(uint32_t tag, uint32_t data); - bool writeDouble(double d); - bool writeBytes(const void *p, size_t nbytes); - bool writeChars(const jschar *p, size_t nchars); - bool writePtr(const void *); - - template - bool writeArray(const T *p, size_t nbytes); - - bool extractBuffer(uint64_t **datap, size_t *sizep); - - uint64_t count() { return buf.length(); } - - private: - JSContext *cx; - js::Vector buf; -}; - -struct SCInput { - public: - SCInput(JSContext *cx, uint64_t *data, size_t nbytes); - - JSContext *context() const { return cx; } - - bool read(uint64_t *p); - bool readPair(uint32_t *tagp, uint32_t *datap); - bool readDouble(double *p); - bool readBytes(void *p, size_t nbytes); - bool readChars(jschar *p, size_t nchars); - bool readPtr(void **); - - bool get(uint64_t *p); - bool getPair(uint32_t *tagp, uint32_t *datap); - - bool replace(uint64_t u); - bool replacePair(uint32_t tag, uint32_t data); - - template - bool readArray(T *p, size_t nelems); - - private: - bool eof(); - - void staticAssertions() { - JS_STATIC_ASSERT(sizeof(jschar) == 2); - JS_STATIC_ASSERT(sizeof(uint32_t) == 4); - JS_STATIC_ASSERT(sizeof(double) == 8); - } - - JSContext *cx; - uint64_t *point; - uint64_t *end; -}; - -} /* namespace js */ - -struct JSStructuredCloneReader { - public: - explicit JSStructuredCloneReader(js::SCInput &in, const JSStructuredCloneCallbacks *cb, - void *cbClosure) - : in(in), objs(in.context()), allObjs(in.context()), - callbacks(cb), closure(cbClosure) { } - - js::SCInput &input() { return in; } - bool read(js::Value *vp); - - private: - JSContext *context() { return in.context(); } - - bool readTransferMap(); - - bool checkDouble(double d); - JSString *readString(uint32_t nchars); - bool readTypedArray(uint32_t arrayType, uint32_t nelems, js::Value *vp, bool v1Read = false); - bool readArrayBuffer(uint32_t nbytes, js::Value *vp); - bool readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems, js::Value *vp); - bool readId(jsid *idp); - bool startRead(js::Value *vp); - - js::SCInput ∈ - - // Stack of objects with properties remaining to be read. - js::AutoValueVector objs; - - // Stack of all objects read during this deserialization - js::AutoValueVector allObjs; - - // The user defined callbacks that will be used for cloning. - const JSStructuredCloneCallbacks *callbacks; - - // Any value passed to JS_ReadStructuredClone. - void *closure; - - friend bool JS_ReadTypedArray(JSStructuredCloneReader *r, jsval *vp); -}; - -struct JSStructuredCloneWriter { - public: - explicit JSStructuredCloneWriter(js::SCOutput &out, - const JSStructuredCloneCallbacks *cb, - void *cbClosure, - jsval tVal) - : out(out), objs(out.context()), - counts(out.context()), ids(out.context()), - memory(out.context()), callbacks(cb), closure(cbClosure), - transferable(out.context(), tVal), transferableObjects(out.context()) { } - - bool init() { return transferableObjects.init() && parseTransferable() && - memory.init() && writeTransferMap(); } - - bool write(const js::Value &v); - - js::SCOutput &output() { return out; } - - private: - JSContext *context() { return out.context(); } - - bool writeTransferMap(); - - bool writeString(uint32_t tag, JSString *str); - bool writeId(jsid id); - bool writeArrayBuffer(JS::HandleObject obj); - bool writeTypedArray(JS::HandleObject obj); - bool startObject(JS::HandleObject obj, bool *backref); - bool startWrite(const js::Value &v); - bool traverseObject(JS::HandleObject obj); - - bool parseTransferable(); - void reportErrorTransferable(); - - inline void checkStack(); - - js::SCOutput &out; - - // Vector of objects with properties remaining to be written. - // - // NB: These can span multiple compartments, so the compartment must be - // entered before any manipulation is performed. - js::AutoValueVector objs; - - // counts[i] is the number of properties of objs[i] remaining to be written. - // counts.length() == objs.length() and sum(counts) == ids.length(). - js::Vector counts; - - // Ids of properties remaining to be written. - js::AutoIdVector ids; - - // The "memory" list described in the HTML5 internal structured cloning algorithm. - // memory is a superset of objs; items are never removed from Memory - // until a serialization operation is finished - typedef js::AutoObjectUnsigned32HashMap CloneMemory; - CloneMemory memory; - - // The user defined callbacks that will be used for cloning. - const JSStructuredCloneCallbacks *callbacks; - - // Any value passed to JS_WriteStructuredClone. - void *closure; - - // List of transferable objects - JS::RootedValue transferable; - js::AutoObjectHashSet transferableObjects; - - friend bool JS_WriteTypedArray(JSStructuredCloneWriter *w, jsval v); -}; - -#endif /* jsclone_h */ diff --git a/js/src/moz.build b/js/src/moz.build index e380dc07b419..2566a6964c84 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -71,6 +71,7 @@ EXPORTS.js += [ '../public/PropertyKey.h', '../public/RequiredDefines.h', '../public/RootingAPI.h', + '../public/StructuredClone.h', '../public/Utility.h', '../public/Value.h', '../public/Vector.h', @@ -125,6 +126,7 @@ CPP_SOURCES += [ 'StoreBuffer.cpp', 'String.cpp', 'StringBuffer.cpp', + 'StructuredClone.cpp', 'TestingFunctions.cpp', 'ThreadPool.cpp', 'TokenStream.cpp', @@ -143,7 +145,6 @@ CPP_SOURCES += [ 'jsarray.cpp', 'jsatom.cpp', 'jsbool.cpp', - 'jsclone.cpp', 'jscntxt.cpp', 'jscompartment.cpp', 'jscrashreport.cpp', diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index d2eb07399b33..d9267463fffa 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -62,6 +62,7 @@ #include "frontend/BytecodeEmitter.h" #include "frontend/Parser.h" #include "jit/Ion.h" +#include "js/StructuredClone.h" #include "perf/jsperf.h" #include "shell/jsheaptools.h" #include "shell/jsoptparse.h" diff --git a/js/src/jsclone.cpp b/js/src/vm/StructuredClone.cpp similarity index 75% rename from js/src/jsclone.cpp rename to js/src/vm/StructuredClone.cpp index dac5ca559883..c3e1dc33b808 100644 --- a/js/src/jsclone.cpp +++ b/js/src/vm/StructuredClone.cpp @@ -27,11 +27,13 @@ * array object. */ -#include "jsclone.h" +#include "js/StructuredClone.h" #include "mozilla/Endian.h" #include "mozilla/FloatingPoint.h" +#include "jsapi.h" +#include "jscntxt.h" #include "jsdate.h" #include "jswrapper.h" @@ -86,6 +88,181 @@ enum TransferableMapHeader { SCTAG_TM_MARKED }; +namespace js { + +struct SCOutput { + public: + explicit SCOutput(JSContext *cx); + + JSContext *context() const { return cx; } + + bool write(uint64_t u); + bool writePair(uint32_t tag, uint32_t data); + bool writeDouble(double d); + bool writeBytes(const void *p, size_t nbytes); + bool writeChars(const jschar *p, size_t nchars); + bool writePtr(const void *); + + template + bool writeArray(const T *p, size_t nbytes); + + bool extractBuffer(uint64_t **datap, size_t *sizep); + + uint64_t count() { return buf.length(); } + + private: + JSContext *cx; + js::Vector buf; +}; + +struct SCInput { + public: + SCInput(JSContext *cx, uint64_t *data, size_t nbytes); + + JSContext *context() const { return cx; } + + bool read(uint64_t *p); + bool readPair(uint32_t *tagp, uint32_t *datap); + bool readDouble(double *p); + bool readBytes(void *p, size_t nbytes); + bool readChars(jschar *p, size_t nchars); + bool readPtr(void **); + + bool get(uint64_t *p); + bool getPair(uint32_t *tagp, uint32_t *datap); + + bool replace(uint64_t u); + bool replacePair(uint32_t tag, uint32_t data); + + template + bool readArray(T *p, size_t nelems); + + private: + bool eof(); + + void staticAssertions() { + JS_STATIC_ASSERT(sizeof(jschar) == 2); + JS_STATIC_ASSERT(sizeof(uint32_t) == 4); + JS_STATIC_ASSERT(sizeof(double) == 8); + } + + JSContext *cx; + uint64_t *point; + uint64_t *end; +}; + +} + +struct JSStructuredCloneReader { + public: + explicit JSStructuredCloneReader(js::SCInput &in, const JSStructuredCloneCallbacks *cb, + void *cbClosure) + : in(in), objs(in.context()), allObjs(in.context()), + callbacks(cb), closure(cbClosure) { } + + js::SCInput &input() { return in; } + bool read(js::Value *vp); + + private: + JSContext *context() { return in.context(); } + + bool readTransferMap(); + + bool checkDouble(double d); + JSString *readString(uint32_t nchars); + bool readTypedArray(uint32_t arrayType, uint32_t nelems, js::Value *vp, bool v1Read = false); + bool readArrayBuffer(uint32_t nbytes, js::Value *vp); + bool readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems, js::Value *vp); + bool readId(jsid *idp); + bool startRead(js::Value *vp); + + js::SCInput ∈ + + // Stack of objects with properties remaining to be read. + js::AutoValueVector objs; + + // Stack of all objects read during this deserialization + js::AutoValueVector allObjs; + + // The user defined callbacks that will be used for cloning. + const JSStructuredCloneCallbacks *callbacks; + + // Any value passed to JS_ReadStructuredClone. + void *closure; + + friend bool JS_ReadTypedArray(JSStructuredCloneReader *r, JS::Value *vp); +}; + +struct JSStructuredCloneWriter { + public: + explicit JSStructuredCloneWriter(js::SCOutput &out, + const JSStructuredCloneCallbacks *cb, + void *cbClosure, + jsval tVal) + : out(out), objs(out.context()), + counts(out.context()), ids(out.context()), + memory(out.context()), callbacks(cb), closure(cbClosure), + transferable(out.context(), tVal), transferableObjects(out.context()) { } + + bool init() { return transferableObjects.init() && parseTransferable() && + memory.init() && writeTransferMap(); } + + bool write(const js::Value &v); + + js::SCOutput &output() { return out; } + + private: + JSContext *context() { return out.context(); } + + bool writeTransferMap(); + + bool writeString(uint32_t tag, JSString *str); + bool writeId(jsid id); + bool writeArrayBuffer(JS::HandleObject obj); + bool writeTypedArray(JS::HandleObject obj); + bool startObject(JS::HandleObject obj, bool *backref); + bool startWrite(const js::Value &v); + bool traverseObject(JS::HandleObject obj); + + bool parseTransferable(); + void reportErrorTransferable(); + + inline void checkStack(); + + js::SCOutput &out; + + // Vector of objects with properties remaining to be written. + // + // NB: These can span multiple compartments, so the compartment must be + // entered before any manipulation is performed. + js::AutoValueVector objs; + + // counts[i] is the number of properties of objs[i] remaining to be written. + // counts.length() == objs.length() and sum(counts) == ids.length(). + js::Vector counts; + + // Ids of properties remaining to be written. + js::AutoIdVector ids; + + // The "memory" list described in the HTML5 internal structured cloning algorithm. + // memory is a superset of objs; items are never removed from Memory + // until a serialization operation is finished + typedef js::AutoObjectUnsigned32HashMap CloneMemory; + CloneMemory memory; + + // The user defined callbacks that will be used for cloning. + const JSStructuredCloneCallbacks *callbacks; + + // Any value passed to JS_WriteStructuredClone. + void *closure; + + // List of transferable objects + JS::RootedValue transferable; + js::AutoObjectHashSet transferableObjects; + + friend bool JS_WriteTypedArray(JSStructuredCloneWriter *w, JS::Value v); +}; + JS_FRIEND_API(uint64_t) js_GetSCOffset(JSStructuredCloneWriter* writer) { @@ -97,10 +274,12 @@ JS_STATIC_ASSERT(SCTAG_END_OF_BUILTIN_TYPES <= JS_SCTAG_USER_MIN); JS_STATIC_ASSERT(JS_SCTAG_USER_MIN <= JS_SCTAG_USER_MAX); JS_STATIC_ASSERT(TypedArrayObject::TYPE_INT8 == 0); +namespace js { + bool -js::WriteStructuredClone(JSContext *cx, HandleValue v, uint64_t **bufp, size_t *nbytesp, - const JSStructuredCloneCallbacks *cb, void *cbClosure, - jsval transferable) +WriteStructuredClone(JSContext *cx, HandleValue v, uint64_t **bufp, size_t *nbytesp, + const JSStructuredCloneCallbacks *cb, void *cbClosure, + jsval transferable) { SCOutput out(cx); JSStructuredCloneWriter w(out, cb, cbClosure, transferable); @@ -108,8 +287,8 @@ js::WriteStructuredClone(JSContext *cx, HandleValue v, uint64_t **bufp, size_t * } bool -js::ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, Value *vp, - const JSStructuredCloneCallbacks *cb, void *cbClosure) +ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, Value *vp, + const JSStructuredCloneCallbacks *cb, void *cbClosure) { SCInput in(cx, data, nbytes); @@ -121,7 +300,7 @@ js::ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, Value *vp, } bool -js::ClearStructuredClone(const uint64_t *data, size_t nbytes) +ClearStructuredClone(const uint64_t *data, size_t nbytes) { const uint64_t *point = data; const uint64_t *end = data + nbytes / 8; @@ -152,7 +331,7 @@ js::ClearStructuredClone(const uint64_t *data, size_t nbytes) } bool -js::StructuredCloneHasTransferObjects(const uint64_t *data, size_t nbytes, bool *hasTransferable) +StructuredCloneHasTransferObjects(const uint64_t *data, size_t nbytes, bool *hasTransferable) { *hasTransferable = false; @@ -167,6 +346,8 @@ js::StructuredCloneHasTransferObjects(const uint64_t *data, size_t nbytes, bool return true; } +} + static inline uint64_t PairToUInt64(uint32_t tag, uint32_t data) { @@ -561,24 +742,6 @@ JSStructuredCloneWriter::checkStack() #endif } -JS_PUBLIC_API(bool) -JS_WriteTypedArray(JSStructuredCloneWriter *w, jsval v) -{ - JS_ASSERT(v.isObject()); - assertSameCompartment(w->context(), v); - RootedObject obj(w->context(), &v.toObject()); - - // If the object is a security wrapper, see if we're allowed to unwrap it. - // If we aren't, throw. - if (obj->is()) - obj = CheckedUnwrap(obj); - if (!obj) { - JS_ReportError(w->context(), "Permission denied to access object"); - return false; - } - return w->writeTypedArray(obj); -} - /* * Write out a typed array. Note that post-v1 structured clone buffers do not * perform endianness conversion on stored data, so multibyte typed arrays @@ -852,26 +1015,6 @@ TagToV1ArrayType(uint32_t tag) return tag - SCTAG_TYPED_ARRAY_V1_MIN; } -JS_PUBLIC_API(bool) -JS_ReadTypedArray(JSStructuredCloneReader *r, jsval *vp) -{ - uint32_t tag, nelems; - if (!r->input().readPair(&tag, &nelems)) - return false; - if (tag >= SCTAG_TYPED_ARRAY_V1_MIN && tag <= SCTAG_TYPED_ARRAY_V1_MAX) { - return r->readTypedArray(TagToV1ArrayType(tag), nelems, vp, true); - } else if (tag == SCTAG_TYPED_ARRAY_OBJECT) { - uint64_t arrayType; - if (!r->input().read(&arrayType)) - return false; - return r->readTypedArray(arrayType, nelems, vp); - } else { - JS_ReportErrorNumber(r->context(), js_GetErrorMessage, NULL, - JSMSG_SC_BAD_SERIALIZED_DATA, "expected type array"); - return false; - } -} - bool JSStructuredCloneReader::readTypedArray(uint32_t arrayType, uint32_t nelems, Value *vp, bool v1Read) @@ -1280,3 +1423,257 @@ JSStructuredCloneReader::read(Value *vp) return true; } +using namespace js; + +JS_PUBLIC_API(bool) +JS_ReadStructuredClone(JSContext *cx, uint64_t *buf, size_t nbytes, + uint32_t version, JS::Value *vp, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure) +{ + AssertHeapIsIdle(cx); + CHECK_REQUEST(cx); + + if (version > JS_STRUCTURED_CLONE_VERSION) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CLONE_VERSION); + return false; + } + const JSStructuredCloneCallbacks *callbacks = + optionalCallbacks ? + optionalCallbacks : + cx->runtime()->structuredCloneCallbacks; + return ReadStructuredClone(cx, buf, nbytes, vp, callbacks, closure); +} + +JS_PUBLIC_API(bool) +JS_WriteStructuredClone(JSContext *cx, JS::Value valueArg, uint64_t **bufp, size_t *nbytesp, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure, JS::Value transferable) +{ + RootedValue value(cx, valueArg); + AssertHeapIsIdle(cx); + CHECK_REQUEST(cx); + assertSameCompartment(cx, value); + + const JSStructuredCloneCallbacks *callbacks = + optionalCallbacks ? + optionalCallbacks : + cx->runtime()->structuredCloneCallbacks; + return WriteStructuredClone(cx, value, bufp, nbytesp, callbacks, closure, transferable); +} + +JS_PUBLIC_API(bool) +JS_ClearStructuredClone(const uint64_t *data, size_t nbytes) +{ + return ClearStructuredClone(data, nbytes); +} + +JS_PUBLIC_API(bool) +JS_StructuredCloneHasTransferables(const uint64_t *data, size_t nbytes, + bool *hasTransferable) +{ + bool transferable; + if (!StructuredCloneHasTransferObjects(data, nbytes, &transferable)) + return false; + + *hasTransferable = transferable; + return true; +} + +JS_PUBLIC_API(bool) +JS_StructuredClone(JSContext *cx, JS::Value valueArg, JS::Value *vp, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure) +{ + RootedValue value(cx, valueArg); + AssertHeapIsIdle(cx); + CHECK_REQUEST(cx); + assertSameCompartment(cx, value); + + const JSStructuredCloneCallbacks *callbacks = + optionalCallbacks ? + optionalCallbacks : + cx->runtime()->structuredCloneCallbacks; + JSAutoStructuredCloneBuffer buf; + return buf.write(cx, value, callbacks, closure) && + buf.read(cx, vp, callbacks, closure); +} + +void +JSAutoStructuredCloneBuffer::clear() +{ + if (data_) { + ClearStructuredClone(data_, nbytes_); + data_ = NULL; + nbytes_ = 0; + version_ = 0; + } +} + +bool +JSAutoStructuredCloneBuffer::copy(const uint64_t *srcData, size_t nbytes, uint32_t version) +{ + // transferable objects cannot be copied + bool hasTransferable; + if (!StructuredCloneHasTransferObjects(data_, nbytes_, &hasTransferable) || + hasTransferable) + return false; + + uint64_t *newData = static_cast(js_malloc(nbytes)); + if (!newData) + return false; + + js_memcpy(newData, srcData, nbytes); + + clear(); + data_ = newData; + nbytes_ = nbytes; + version_ = version; + return true; +} + +void +JSAutoStructuredCloneBuffer::adopt(uint64_t *data, size_t nbytes, uint32_t version) +{ + clear(); + data_ = data; + nbytes_ = nbytes; + version_ = version; +} + +void +JSAutoStructuredCloneBuffer::steal(uint64_t **datap, size_t *nbytesp, uint32_t *versionp) +{ + *datap = data_; + *nbytesp = nbytes_; + if (versionp) + *versionp = version_; + + data_ = NULL; + nbytes_ = 0; + version_ = 0; +} + +bool +JSAutoStructuredCloneBuffer::read(JSContext *cx, JS::Value *vp, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure) +{ + JS_ASSERT(cx); + JS_ASSERT(data_); + return !!JS_ReadStructuredClone(cx, data_, nbytes_, version_, vp, + optionalCallbacks, closure); +} + +bool +JSAutoStructuredCloneBuffer::write(JSContext *cx, JS::Value valueArg, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure) +{ + JS::Value transferable = JSVAL_VOID; + return write(cx, valueArg, transferable, optionalCallbacks, closure); +} + +bool +JSAutoStructuredCloneBuffer::write(JSContext *cx, JS::Value valueArg, + JS::Value transferable, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure) +{ + RootedValue value(cx, valueArg); + clear(); + bool ok = !!JS_WriteStructuredClone(cx, value, &data_, &nbytes_, + optionalCallbacks, closure, + transferable); + if (!ok) { + data_ = NULL; + nbytes_ = 0; + version_ = JS_STRUCTURED_CLONE_VERSION; + } + return ok; +} + +void +JSAutoStructuredCloneBuffer::swap(JSAutoStructuredCloneBuffer &other) +{ + uint64_t *data = other.data_; + size_t nbytes = other.nbytes_; + uint32_t version = other.version_; + + other.data_ = this->data_; + other.nbytes_ = this->nbytes_; + other.version_ = this->version_; + + this->data_ = data; + this->nbytes_ = nbytes; + this->version_ = version; +} + +JS_PUBLIC_API(void) +JS_SetStructuredCloneCallbacks(JSRuntime *rt, const JSStructuredCloneCallbacks *callbacks) +{ + rt->structuredCloneCallbacks = callbacks; +} + +JS_PUBLIC_API(bool) +JS_ReadUint32Pair(JSStructuredCloneReader *r, uint32_t *p1, uint32_t *p2) +{ + return r->input().readPair((uint32_t *) p1, (uint32_t *) p2); +} + +JS_PUBLIC_API(bool) +JS_ReadBytes(JSStructuredCloneReader *r, void *p, size_t len) +{ + return r->input().readBytes(p, len); +} + +JS_PUBLIC_API(bool) +JS_ReadTypedArray(JSStructuredCloneReader *r, JS::Value *vp) +{ + uint32_t tag, nelems; + if (!r->input().readPair(&tag, &nelems)) + return false; + if (tag >= SCTAG_TYPED_ARRAY_V1_MIN && tag <= SCTAG_TYPED_ARRAY_V1_MAX) { + return r->readTypedArray(TagToV1ArrayType(tag), nelems, vp, true); + } else if (tag == SCTAG_TYPED_ARRAY_OBJECT) { + uint64_t arrayType; + if (!r->input().read(&arrayType)) + return false; + return r->readTypedArray(arrayType, nelems, vp); + } else { + JS_ReportErrorNumber(r->context(), js_GetErrorMessage, NULL, + JSMSG_SC_BAD_SERIALIZED_DATA, "expected type array"); + return false; + } +} + +JS_PUBLIC_API(bool) +JS_WriteUint32Pair(JSStructuredCloneWriter *w, uint32_t tag, uint32_t data) +{ + return w->output().writePair(tag, data); +} + +JS_PUBLIC_API(bool) +JS_WriteBytes(JSStructuredCloneWriter *w, const void *p, size_t len) +{ + return w->output().writeBytes(p, len); +} + +JS_PUBLIC_API(bool) +JS_WriteTypedArray(JSStructuredCloneWriter *w, JS::Value v) +{ + JS_ASSERT(v.isObject()); + assertSameCompartment(w->context(), v); + RootedObject obj(w->context(), &v.toObject()); + + // If the object is a security wrapper, see if we're allowed to unwrap it. + // If we aren't, throw. + if (obj->is()) + obj = CheckedUnwrap(obj); + if (!obj) { + JS_ReportError(w->context(), "Permission denied to access object"); + return false; + } + return w->writeTypedArray(obj); +} + From 5c94c32440f4a712c762c210515536d352422636 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 19 Aug 2013 23:45:26 -0700 Subject: [PATCH 25/54] Bug 905017 (part 3) - Move profiling stack stuff from jsapi.h to js/ProfilingStack.h. r=billm. --HG-- extra : rebase_source : 8322f1c96b95685912375484172d47f53107947f --- config/check_spidermonkey_style.py | 39 +++++++--- js/ipc/JavaScriptParent.h | 1 + js/public/ProfilingStack.h | 94 +++++++++++++++++++++++ js/public/Value.h | 7 ++ js/src/config/check_spidermonkey_style.py | 39 +++++++--- js/src/jsapi.cpp | 9 --- js/src/jsapi.h | 3 - js/src/jsfriendapi.cpp | 18 ----- js/src/jsfriendapi.h | 78 ------------------- js/src/moz.build | 2 + js/src/vm/CharacterEncoding.cpp | 4 +- js/src/vm/SPSProfiler.cpp | 27 ++++++- js/src/vm/SPSProfiler.h | 2 + js/src/vm/Value.cpp | 21 +++++ js/xpconnect/public/nsAutoJSValHolder.h | 21 ++--- security/manager/ssl/src/nsNSSIOLayer.cpp | 1 + tools/profiler/PseudoStack.h | 2 +- 17 files changed, 221 insertions(+), 147 deletions(-) create mode 100644 js/public/ProfilingStack.h create mode 100644 js/src/vm/Value.cpp diff --git a/config/check_spidermonkey_style.py b/config/check_spidermonkey_style.py index c7e8d2934c6f..accccdbdc560 100644 --- a/config/check_spidermonkey_style.py +++ b/config/check_spidermonkey_style.py @@ -302,6 +302,28 @@ def module_name(name): return name.replace('inlines.h', '').replace('-inl.h', '').replace('.h', '').replace('.cpp', '') +def is_module_header(enclosing_inclname, header_inclname): + '''Determine if an included name is the "module header", i.e. should be + first in the file.''' + + module = module_name(enclosing_inclname) + + # Normal case, e.g. module == "foo/Bar", header_inclname == "foo/Bar.h". + if module == module_name(header_inclname): + return True + + # A public header, e.g. module == "foo/Bar", header_inclname == "js/Bar.h". + m = re.match(r'js\/(.*)\.h', header_inclname) + if m is not None and module.endswith('/' + m.group(1)): + return True + + # A weird public header case. + if module == 'jsmemorymetrics' and header_inclname == 'js/MemoryMetrics.h': + return True + + return False + + class Include(object): '''Important information for a single #include statement.''' @@ -313,7 +335,7 @@ class Include(object): def isLeaf(self): return True - def section(self, module): + def section(self, enclosing_inclname): '''Identify which section inclname belongs to. The section numbers are as follows. @@ -333,12 +355,9 @@ class Include(object): if not self.inclname.endswith('.h'): return 7 - # A couple of modules have the .h file in js/ and the .cpp file elsewhere and so need special - # handling. - if module == module_name(self.inclname) or \ - module == 'jsmemorymetrics' and self.inclname == 'js/MemoryMetrics.h' or \ - module == 'vm/PropertyKey' and self.inclname == 'js/PropertyKey.h' or \ - module == 'vm/StructuredClone' and self.inclname == 'js/StructuredClone.h': + # A couple of modules have the .h file in js/ and the .cpp file elsewhere and so need + # special handling. + if is_module_header(enclosing_inclname, self.inclname): return 0 if '/' in self.inclname: @@ -452,8 +471,6 @@ def do_file(filename, inclname, file_kind, f, all_inclnames, included_h_inclname if inclname == include.inclname: error(filename, include.linenum, 'the file includes itself') - module = module_name(inclname) - def check_includes_order(include1, include2): '''Check the ordering of two #include statements.''' @@ -461,8 +478,8 @@ def do_file(filename, inclname, file_kind, f, all_inclnames, included_h_inclname include2.inclname in oddly_ordered_inclnames: return - section1 = include1.section(module) - section2 = include2.section(module) + section1 = include1.section(inclname) + section2 = include2.section(inclname) if (section1 > section2) or \ ((section1 == section2) and (include1.inclname.lower() > include2.inclname.lower())): error(filename, str(include1.linenum) + ':' + str(include2.linenum), diff --git a/js/ipc/JavaScriptParent.h b/js/ipc/JavaScriptParent.h index 041149e3dde8..afd18b6652e5 100644 --- a/js/ipc/JavaScriptParent.h +++ b/js/ipc/JavaScriptParent.h @@ -10,6 +10,7 @@ #include "JavaScriptShared.h" #include "mozilla/jsipc/PJavaScriptParent.h" +#include "jsclass.h" #ifdef XP_WIN #undef GetClassName diff --git a/js/public/ProfilingStack.h b/js/public/ProfilingStack.h new file mode 100644 index 000000000000..36af5969034d --- /dev/null +++ b/js/public/ProfilingStack.h @@ -0,0 +1,94 @@ +/* -*- 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 js_ProfilingStack_h +#define js_ProfilingStack_h + +#include "jsbytecode.h" +#include "jstypes.h" + +#include "js/Utility.h" + +struct JSRuntime; +class JSScript; + +namespace js { + +// A call stack can be specified to the JS engine such that all JS entry/exits +// to functions push/pop an entry to/from the specified stack. +// +// For more detailed information, see vm/SPSProfiler.h. +// +class ProfileEntry +{ + // All fields are marked volatile to prevent the compiler from re-ordering + // instructions. Namely this sequence: + // + // 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 NULL 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 + // representation of the stack is stored such that all ProfileEntry + // instances are volatile. These methods would not be available unless they + // were marked as volatile as well. + + bool js() volatile { + JS_ASSERT_IF(sp == NULL, script_ != NULL); + return sp == NULL; + } + + uint32_t line() volatile { JS_ASSERT(!js()); return idx; } + JSScript *script() volatile { JS_ASSERT(js()); return script_; } + void *stackAddress() volatile { return sp; } + const char *label() volatile { return string; } + + void setLine(uint32_t aLine) volatile { JS_ASSERT(!js()); idx = aLine; } + void setLabel(const char *aString) volatile { string = aString; } + void setStackAddress(void *aSp) volatile { sp = aSp; } + void setScript(JSScript *aScript) volatile { script_ = aScript; } + + // We can't know the layout of JSScript, so look in vm/SPSProfiler.cpp. + JS_FRIEND_API(jsbytecode *) pc() volatile; + JS_FRIEND_API(void) setPC(jsbytecode *pc) volatile; + + static size_t offsetOfString() { return offsetof(ProfileEntry, string); } + static size_t offsetOfStackAddress() { return offsetof(ProfileEntry, sp); } + static size_t offsetOfPCIdx() { return offsetof(ProfileEntry, idx); } + static size_t offsetOfScript() { return offsetof(ProfileEntry, script_); } + + // The index used in the entry can either be a line number or the offset of + // a pc into a script's code. To signify a NULL pc, use a -1 index. This is + // checked against in pc() and setPC() to set/get the right pc. + static const int32_t NullPCIndex = -1; +}; + +JS_FRIEND_API(void) +SetRuntimeProfilingStack(JSRuntime *rt, ProfileEntry *stack, uint32_t *size, + uint32_t max); + +JS_FRIEND_API(void) +EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled); + +JS_FRIEND_API(jsbytecode*) +ProfilingGetPC(JSRuntime *rt, JSScript *script, void *ip); + +} // namespace js + +#endif /* js_ProfilingStack_h */ diff --git a/js/public/Value.h b/js/public/Value.h index 394c25e09e8c..57ec128e9b29 100644 --- a/js/public/Value.h +++ b/js/public/Value.h @@ -1907,6 +1907,13 @@ extern JS_PUBLIC_DATA(const jsval) JSVAL_FALSE; extern JS_PUBLIC_DATA(const jsval) JSVAL_TRUE; extern JS_PUBLIC_DATA(const jsval) JSVAL_VOID; +namespace JS { + +extern JS_PUBLIC_DATA(const Handle) NullHandleValue; +extern JS_PUBLIC_DATA(const Handle) UndefinedHandleValue; + +} + #undef JS_VALUE_IS_CONSTEXPR #undef JS_RETURN_LAYOUT_FROM_BITS diff --git a/js/src/config/check_spidermonkey_style.py b/js/src/config/check_spidermonkey_style.py index c7e8d2934c6f..accccdbdc560 100644 --- a/js/src/config/check_spidermonkey_style.py +++ b/js/src/config/check_spidermonkey_style.py @@ -302,6 +302,28 @@ def module_name(name): return name.replace('inlines.h', '').replace('-inl.h', '').replace('.h', '').replace('.cpp', '') +def is_module_header(enclosing_inclname, header_inclname): + '''Determine if an included name is the "module header", i.e. should be + first in the file.''' + + module = module_name(enclosing_inclname) + + # Normal case, e.g. module == "foo/Bar", header_inclname == "foo/Bar.h". + if module == module_name(header_inclname): + return True + + # A public header, e.g. module == "foo/Bar", header_inclname == "js/Bar.h". + m = re.match(r'js\/(.*)\.h', header_inclname) + if m is not None and module.endswith('/' + m.group(1)): + return True + + # A weird public header case. + if module == 'jsmemorymetrics' and header_inclname == 'js/MemoryMetrics.h': + return True + + return False + + class Include(object): '''Important information for a single #include statement.''' @@ -313,7 +335,7 @@ class Include(object): def isLeaf(self): return True - def section(self, module): + def section(self, enclosing_inclname): '''Identify which section inclname belongs to. The section numbers are as follows. @@ -333,12 +355,9 @@ class Include(object): if not self.inclname.endswith('.h'): return 7 - # A couple of modules have the .h file in js/ and the .cpp file elsewhere and so need special - # handling. - if module == module_name(self.inclname) or \ - module == 'jsmemorymetrics' and self.inclname == 'js/MemoryMetrics.h' or \ - module == 'vm/PropertyKey' and self.inclname == 'js/PropertyKey.h' or \ - module == 'vm/StructuredClone' and self.inclname == 'js/StructuredClone.h': + # A couple of modules have the .h file in js/ and the .cpp file elsewhere and so need + # special handling. + if is_module_header(enclosing_inclname, self.inclname): return 0 if '/' in self.inclname: @@ -452,8 +471,6 @@ def do_file(filename, inclname, file_kind, f, all_inclnames, included_h_inclname if inclname == include.inclname: error(filename, include.linenum, 'the file includes itself') - module = module_name(inclname) - def check_includes_order(include1, include2): '''Check the ordering of two #include statements.''' @@ -461,8 +478,8 @@ def do_file(filename, inclname, file_kind, f, all_inclnames, included_h_inclname include2.inclname in oddly_ordered_inclnames: return - section1 = include1.section(module) - section2 = include2.section(module) + section1 = include1.section(inclname) + section2 = include2.section(inclname) if (section1 > section2) or \ ((section1 == section2) and (include1.inclname.lower() > include2.inclname.lower())): error(filename, str(include1.linenum) + ':' + str(include2.linenum), diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 96c97e6de95f..b556031fb4cf 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -128,15 +128,6 @@ const jsid JSID_VOID = { size_t(JSID_TYPE_VOID) }; const jsid JSID_EMPTY = { size_t(JSID_TYPE_OBJECT) }; #endif -const jsval JSVAL_NULL = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_NULL, 0)); -const jsval JSVAL_ZERO = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_INT32, 0)); -const jsval JSVAL_ONE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_INT32, 1)); -const jsval JSVAL_FALSE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_BOOLEAN, false)); -const jsval JSVAL_TRUE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_BOOLEAN, true)); -const jsval JSVAL_VOID = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_UNDEFINED, 0)); -const HandleValue JS::NullHandleValue = HandleValue::fromMarkedLocation(&JSVAL_NULL); -const HandleValue JS::UndefinedHandleValue = HandleValue::fromMarkedLocation(&JSVAL_VOID); - const jsid voidIdValue = JSID_VOID; const jsid emptyIdValue = JSID_EMPTY; const HandleId JS::JSID_VOIDHANDLE = HandleId::fromMarkedLocation(&voidIdValue); diff --git a/js/src/jsapi.h b/js/src/jsapi.h index b27ee2c39621..b7e625aeca12 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -5087,9 +5087,6 @@ JS_DecodeInterpretedFunction(JSContext *cx, const void *data, uint32_t length, namespace JS { -extern JS_PUBLIC_DATA(const Handle) NullHandleValue; -extern JS_PUBLIC_DATA(const Handle) UndefinedHandleValue; - extern JS_PUBLIC_DATA(const Handle) JSID_VOIDHANDLE; extern JS_PUBLIC_DATA(const Handle) JSID_EMPTYHANDLE; diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index a85fa6355664..ef25ca501246 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -991,24 +991,6 @@ js::GetEnterCompartmentDepth(JSContext *cx) } #endif -JS_FRIEND_API(void) -js::SetRuntimeProfilingStack(JSRuntime *rt, ProfileEntry *stack, uint32_t *size, uint32_t max) -{ - rt->spsProfiler.setProfilingStack(stack, size, max); -} - -JS_FRIEND_API(void) -js::EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled) -{ - rt->spsProfiler.enable(enabled); -} - -JS_FRIEND_API(jsbytecode*) -js::ProfilingGetPC(JSRuntime *rt, JSScript *script, void *ip) -{ - return rt->spsProfiler.ipToPC(script, size_t(ip)); -} - JS_FRIEND_API(void) js::SetDOMCallbacks(JSRuntime *rt, const DOMCallbacks *callbacks) { diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index b2b377880235..b76c3cf36319 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -669,84 +669,6 @@ GetPCCountScriptSummary(JSContext *cx, size_t script); JS_FRIEND_API(JSString *) GetPCCountScriptContents(JSContext *cx, size_t script); -/* - * A call stack can be specified to the JS engine such that all JS entry/exits - * to functions push/pop an entry to/from the specified stack. - * - * For more detailed information, see vm/SPSProfiler.h - */ -class ProfileEntry -{ - /* - * All fields are marked volatile to prevent the compiler from re-ordering - * instructions. Namely this sequence: - * - * 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 NULL 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 - * representation of the stack is stored such that all ProfileEntry - * instances are volatile. These methods would not be available unless they - * were marked as volatile as well - */ - - bool js() volatile { - JS_ASSERT_IF(sp == NULL, script_ != NULL); - return sp == NULL; - } - - uint32_t line() volatile { JS_ASSERT(!js()); return idx; } - JSScript *script() volatile { JS_ASSERT(js()); return script_; } - void *stackAddress() volatile { return sp; } - const char *label() volatile { return string; } - - void setLine(uint32_t aLine) volatile { JS_ASSERT(!js()); idx = aLine; } - void setLabel(const char *aString) volatile { string = aString; } - void setStackAddress(void *aSp) volatile { sp = aSp; } - void setScript(JSScript *aScript) volatile { script_ = aScript; } - - /* we can't know the layout of JSScript, so look in vm/SPSProfiler.cpp */ - JS_FRIEND_API(jsbytecode *) pc() volatile; - JS_FRIEND_API(void) setPC(jsbytecode *pc) volatile; - - static size_t offsetOfString() { return offsetof(ProfileEntry, string); } - static size_t offsetOfStackAddress() { return offsetof(ProfileEntry, sp); } - static size_t offsetOfPCIdx() { return offsetof(ProfileEntry, idx); } - static size_t offsetOfScript() { return offsetof(ProfileEntry, script_); } - - /* - * The index used in the entry can either be a line number or the offset of - * a pc into a script's code. To signify a NULL pc, use a -1 index. This is - * checked against in pc() and setPC() to set/get the right pc. - */ - static const int32_t NullPCIndex = -1; -}; - -JS_FRIEND_API(void) -SetRuntimeProfilingStack(JSRuntime *rt, ProfileEntry *stack, uint32_t *size, - uint32_t max); - -JS_FRIEND_API(void) -EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled); - -JS_FRIEND_API(jsbytecode*) -ProfilingGetPC(JSRuntime *rt, JSScript *script, void *ip); - #ifdef JS_THREADSAFE JS_FRIEND_API(bool) ContextHasOutstandingRequests(const JSContext *cx); diff --git a/js/src/moz.build b/js/src/moz.build index 2566a6964c84..87ee3b53a51a 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -68,6 +68,7 @@ EXPORTS.js += [ '../public/HeapAPI.h', '../public/LegacyIntTypes.h', '../public/MemoryMetrics.h', + '../public/ProfilingStack.h', '../public/PropertyKey.h', '../public/RequiredDefines.h', '../public/RootingAPI.h', @@ -132,6 +133,7 @@ CPP_SOURCES += [ 'TokenStream.cpp', 'TypedArrayObject.cpp', 'Unicode.cpp', + 'Value.cpp', 'Verifier.cpp', 'Xdr.cpp', 'YarrCanonicalizeUCS2.cpp', diff --git a/js/src/vm/CharacterEncoding.cpp b/js/src/vm/CharacterEncoding.cpp index 222caf14c08b..6e7a7f4ad17f 100644 --- a/js/src/vm/CharacterEncoding.cpp +++ b/js/src/vm/CharacterEncoding.cpp @@ -4,11 +4,11 @@ * 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 "js/CharacterEncoding.h" + #include "jscntxt.h" #include "jsprf.h" -#include "js/CharacterEncoding.h" - using namespace JS; Latin1CharsZ diff --git a/js/src/vm/SPSProfiler.cpp b/js/src/vm/SPSProfiler.cpp index 4d3723e92ff1..4dffb3976b33 100644 --- a/js/src/vm/SPSProfiler.cpp +++ b/js/src/vm/SPSProfiler.cpp @@ -265,14 +265,33 @@ SPSEntryMarker::~SPSEntryMarker() } JS_FRIEND_API(jsbytecode*) -ProfileEntry::pc() volatile { +ProfileEntry::pc() volatile +{ JS_ASSERT_IF(idx != NullPCIndex, idx >= 0 && uint32_t(idx) < script()->length); return idx == NullPCIndex ? NULL : script()->code + idx; } JS_FRIEND_API(void) -ProfileEntry::setPC(jsbytecode *pc) volatile { - JS_ASSERT_IF(pc != NULL, script()->code <= pc && - pc < script()->code + script()->length); +ProfileEntry::setPC(jsbytecode *pc) volatile +{ + JS_ASSERT_IF(pc != NULL, script()->code <= pc && pc < script()->code + script()->length); idx = pc == NULL ? NullPCIndex : pc - script()->code; } + +JS_FRIEND_API(void) +js::SetRuntimeProfilingStack(JSRuntime *rt, ProfileEntry *stack, uint32_t *size, uint32_t max) +{ + rt->spsProfiler.setProfilingStack(stack, size, max); +} + +JS_FRIEND_API(void) +js::EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled) +{ + rt->spsProfiler.enable(enabled); +} + +JS_FRIEND_API(jsbytecode*) +js::ProfilingGetPC(JSRuntime *rt, JSScript *script, void *ip) +{ + return rt->spsProfiler.ipToPC(script, size_t(ip)); +} diff --git a/js/src/vm/SPSProfiler.h b/js/src/vm/SPSProfiler.h index c8a1e345789e..224cd558da3a 100644 --- a/js/src/vm/SPSProfiler.h +++ b/js/src/vm/SPSProfiler.h @@ -14,6 +14,8 @@ #include "jsscript.h" +#include "js/ProfilingStack.h" + /* * SPS Profiler integration with the JS Engine * https://developer.mozilla.org/en/Performance/Profiling_with_the_Built-in_Profiler diff --git a/js/src/vm/Value.cpp b/js/src/vm/Value.cpp new file mode 100644 index 000000000000..d88760b07600 --- /dev/null +++ b/js/src/vm/Value.cpp @@ -0,0 +1,21 @@ +/* -*- 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 "js/Value.h" + +const jsval JSVAL_NULL = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_NULL, 0)); +const jsval JSVAL_ZERO = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_INT32, 0)); +const jsval JSVAL_ONE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_INT32, 1)); +const jsval JSVAL_FALSE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_BOOLEAN, false)); +const jsval JSVAL_TRUE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_BOOLEAN, true)); +const jsval JSVAL_VOID = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_UNDEFINED, 0)); + +namespace JS { + +const HandleValue NullHandleValue = HandleValue::fromMarkedLocation(&JSVAL_NULL); +const HandleValue UndefinedHandleValue = HandleValue::fromMarkedLocation(&JSVAL_VOID); + +} // namespace JS diff --git a/js/xpconnect/public/nsAutoJSValHolder.h b/js/xpconnect/public/nsAutoJSValHolder.h index 54fe8f3be880..4a737f33eee3 100644 --- a/js/xpconnect/public/nsAutoJSValHolder.h +++ b/js/xpconnect/public/nsAutoJSValHolder.h @@ -7,9 +7,10 @@ #define __NSAUTOJSVALHOLDER_H__ #include "nsDebug.h" +#include "jsapi.h" /** - * Simple class that looks and acts like a jsval except that it unroots + * Simple class that looks and acts like a JS::Value except that it unroots * itself automatically if Root() is ever called. Designed to be rooted on the * context or runtime (but not both!). */ @@ -44,7 +45,7 @@ public: else { this->Release(); } - *this = static_cast(aOther); + *this = static_cast(aOther); } return *this; } @@ -76,10 +77,10 @@ public: /** * Manually release, nullifying mVal, and mRt, but returning - * the original jsval. + * the original JS::Value. */ - jsval Release() { - jsval oldval = mVal; + JS::Value Release() { + JS::Value oldval = mVal; if (mRt) { JS_RemoveValueRootRT(mRt, &mVal); // infallible @@ -107,20 +108,20 @@ public: : nullptr; } - jsval* ToJSValPtr() { + JS::Value* ToJSValPtr() { return &mVal; } /** - * Pretend to be a jsval. + * Pretend to be a JS::Value. */ - operator jsval() const { return mVal; } + operator JS::Value() const { return mVal; } nsAutoJSValHolder &operator=(JSObject* aOther) { return *this = OBJECT_TO_JSVAL(aOther); } - nsAutoJSValHolder &operator=(jsval aOther) { + nsAutoJSValHolder &operator=(JS::Value aOther) { #ifdef DEBUG if (JSVAL_IS_GCTHING(aOther) && !JSVAL_IS_NULL(aOther)) { MOZ_ASSERT(IsHeld(), "Not rooted!"); @@ -131,7 +132,7 @@ public: } private: - jsval mVal; + JS::Value mVal; JSRuntime* mRt; }; diff --git a/security/manager/ssl/src/nsNSSIOLayer.cpp b/security/manager/ssl/src/nsNSSIOLayer.cpp index 6c42053ac1ad..41337b9985dd 100644 --- a/security/manager/ssl/src/nsNSSIOLayer.cpp +++ b/security/manager/ssl/src/nsNSSIOLayer.cpp @@ -7,6 +7,7 @@ #include "nsNSSComponent.h" #include "nsNSSIOLayer.h" +#include "mozilla/Casting.h" #include "mozilla/DebugOnly.h" #include "mozilla/Telemetry.h" diff --git a/tools/profiler/PseudoStack.h b/tools/profiler/PseudoStack.h index a59eb9937b01..66549bfec70c 100644 --- a/tools/profiler/PseudoStack.h +++ b/tools/profiler/PseudoStack.h @@ -8,7 +8,7 @@ #include "mozilla/NullPtr.h" #include -#include "jsfriendapi.h" +#include "js/ProfilingStack.h" #include #include From 8040c0cadd2e27c724a06b37116dbb9c5c7de71e Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Thu, 22 Aug 2013 10:16:30 +0200 Subject: [PATCH 26/54] Backout changesets 7e6fb33fdf22:c85332df4320 (bug 905017) for windows bustage. --- config/check_spidermonkey_style.py | 38 +- dom/base/StructuredCloneTags.h | 2 - dom/base/nsStructuredCloneContainer.cpp | 1 - dom/bluetooth/BluetoothReplyRunnable.h | 2 +- dom/indexedDB/IndexedDatabase.h | 2 +- dom/indexedDB/Key.h | 10 +- dom/ipc/StructuredCloneUtils.cpp | 1 - dom/ipc/StructuredCloneUtils.h | 2 +- dom/workers/Events.h | 2 +- dom/workers/XMLHttpRequest.h | 2 - ipc/glue/IPCMessageUtils.h | 2 +- js/ipc/JavaScriptParent.h | 1 - js/public/ProfilingStack.h | 94 ---- js/public/StructuredClone.h | 162 ------ js/public/Value.h | 18 - js/src/config/check_spidermonkey_style.py | 38 +- js/src/jsapi.cpp | 235 ++++++++- js/src/jsapi.h | 183 ++++++- .../{vm/StructuredClone.cpp => jsclone.cpp} | 489 ++---------------- js/src/jsclone.h | 206 ++++++++ js/src/jsfriendapi.cpp | 18 + js/src/jsfriendapi.h | 78 +++ js/src/moz.build | 5 +- js/src/shell/js.cpp | 1 - js/src/vm/CharacterEncoding.cpp | 4 +- js/src/vm/SPSProfiler.cpp | 27 +- js/src/vm/SPSProfiler.h | 2 - js/src/vm/Value.cpp | 21 - js/xpconnect/public/nsAutoJSValHolder.h | 21 +- security/manager/ssl/src/nsNSSIOLayer.cpp | 1 - tools/profiler/PseudoStack.h | 2 +- 31 files changed, 800 insertions(+), 870 deletions(-) delete mode 100644 js/public/ProfilingStack.h delete mode 100644 js/public/StructuredClone.h rename js/src/{vm/StructuredClone.cpp => jsclone.cpp} (75%) create mode 100644 js/src/jsclone.h delete mode 100644 js/src/vm/Value.cpp diff --git a/config/check_spidermonkey_style.py b/config/check_spidermonkey_style.py index accccdbdc560..93e9e8a68877 100644 --- a/config/check_spidermonkey_style.py +++ b/config/check_spidermonkey_style.py @@ -302,28 +302,6 @@ def module_name(name): return name.replace('inlines.h', '').replace('-inl.h', '').replace('.h', '').replace('.cpp', '') -def is_module_header(enclosing_inclname, header_inclname): - '''Determine if an included name is the "module header", i.e. should be - first in the file.''' - - module = module_name(enclosing_inclname) - - # Normal case, e.g. module == "foo/Bar", header_inclname == "foo/Bar.h". - if module == module_name(header_inclname): - return True - - # A public header, e.g. module == "foo/Bar", header_inclname == "js/Bar.h". - m = re.match(r'js\/(.*)\.h', header_inclname) - if m is not None and module.endswith('/' + m.group(1)): - return True - - # A weird public header case. - if module == 'jsmemorymetrics' and header_inclname == 'js/MemoryMetrics.h': - return True - - return False - - class Include(object): '''Important information for a single #include statement.''' @@ -335,7 +313,7 @@ class Include(object): def isLeaf(self): return True - def section(self, enclosing_inclname): + def section(self, module): '''Identify which section inclname belongs to. The section numbers are as follows. @@ -355,9 +333,11 @@ class Include(object): if not self.inclname.endswith('.h'): return 7 - # A couple of modules have the .h file in js/ and the .cpp file elsewhere and so need - # special handling. - if is_module_header(enclosing_inclname, self.inclname): + # A couple of modules have the .h file in js/ and the .cpp file elsewhere and so need special + # handling. + if module == module_name(self.inclname) or \ + module == 'jsmemorymetrics' and self.inclname == 'js/MemoryMetrics.h' or \ + module == 'vm/PropertyKey' and self.inclname == 'js/PropertyKey.h': return 0 if '/' in self.inclname: @@ -471,6 +451,8 @@ def do_file(filename, inclname, file_kind, f, all_inclnames, included_h_inclname if inclname == include.inclname: error(filename, include.linenum, 'the file includes itself') + module = module_name(inclname) + def check_includes_order(include1, include2): '''Check the ordering of two #include statements.''' @@ -478,8 +460,8 @@ def do_file(filename, inclname, file_kind, f, all_inclnames, included_h_inclname include2.inclname in oddly_ordered_inclnames: return - section1 = include1.section(inclname) - section2 = include2.section(inclname) + section1 = include1.section(module) + section2 = include2.section(module) if (section1 > section2) or \ ((section1 == section2) and (include1.inclname.lower() > include2.inclname.lower())): error(filename, str(include1.linenum) + ':' + str(include2.linenum), diff --git a/dom/base/StructuredCloneTags.h b/dom/base/StructuredCloneTags.h index aa3781b2a3e6..4246b46ea139 100644 --- a/dom/base/StructuredCloneTags.h +++ b/dom/base/StructuredCloneTags.h @@ -5,8 +5,6 @@ #ifndef StructuredCloneTags_h__ #define StructuredCloneTags_h__ -#include "js/StructuredClone.h" - namespace mozilla { namespace dom { diff --git a/dom/base/nsStructuredCloneContainer.cpp b/dom/base/nsStructuredCloneContainer.cpp index 8cacc5928031..9f9bee55f309 100644 --- a/dom/base/nsStructuredCloneContainer.cpp +++ b/dom/base/nsStructuredCloneContainer.cpp @@ -14,7 +14,6 @@ #include "nsServiceManagerUtils.h" #include "nsContentUtils.h" #include "jsapi.h" -#include "js/StructuredClone.h" #include "mozilla/Base64.h" diff --git a/dom/bluetooth/BluetoothReplyRunnable.h b/dom/bluetooth/BluetoothReplyRunnable.h index e700b5fbb069..8124ba94005b 100644 --- a/dom/bluetooth/BluetoothReplyRunnable.h +++ b/dom/bluetooth/BluetoothReplyRunnable.h @@ -10,7 +10,7 @@ #include "mozilla/Attributes.h" #include "BluetoothCommon.h" #include "nsThreadUtils.h" -#include "js/Value.h" +#include "jsapi.h" class nsIDOMDOMRequest; diff --git a/dom/indexedDB/IndexedDatabase.h b/dom/indexedDB/IndexedDatabase.h index 582dfc34c390..b0ea8965f6c9 100644 --- a/dom/indexedDB/IndexedDatabase.h +++ b/dom/indexedDB/IndexedDatabase.h @@ -10,7 +10,7 @@ #include "nsIProgrammingLanguage.h" #include "mozilla/Attributes.h" -#include "js/StructuredClone.h" +#include "jsapi.h" #include "nsAutoPtr.h" #include "nsCOMPtr.h" #include "nsDebug.h" diff --git a/dom/indexedDB/Key.h b/dom/indexedDB/Key.h index 8b4932193b10..2ce40d03497b 100644 --- a/dom/indexedDB/Key.h +++ b/dom/indexedDB/Key.h @@ -11,8 +11,6 @@ #include "mozIStorageStatement.h" -#include "js/Value.h" - namespace IPC { template struct ParamTraits; } // namespace IPC @@ -161,7 +159,7 @@ public: } nsresult SetFromJSVal(JSContext* aCx, - const JS::Value aVal) + const jsval aVal) { mBuffer.Truncate(); @@ -211,7 +209,7 @@ public: nsresult AppendItem(JSContext* aCx, bool aFirstOfArray, - const JS::Value aVal) + const jsval aVal) { nsresult rv = EncodeJSVal(aCx, aVal, aFirstOfArray ? eMaxType : 0); if (NS_FAILED(rv)) { @@ -305,7 +303,7 @@ private: } // Encoding functions. These append the encoded value to the end of mBuffer - inline nsresult EncodeJSVal(JSContext* aCx, const JS::Value aVal, + inline nsresult EncodeJSVal(JSContext* aCx, const jsval aVal, uint8_t aTypeOffset) { return EncodeJSValInternal(aCx, aVal, aTypeOffset, 0); @@ -331,7 +329,7 @@ private: nsCString mBuffer; private: - nsresult EncodeJSValInternal(JSContext* aCx, const JS::Value aVal, + nsresult EncodeJSValInternal(JSContext* aCx, const jsval aVal, uint8_t aTypeOffset, uint16_t aRecursionDepth); static nsresult DecodeJSValInternal(const unsigned char*& aPos, diff --git a/dom/ipc/StructuredCloneUtils.cpp b/dom/ipc/StructuredCloneUtils.cpp index 00f64235d936..39c0507b184e 100644 --- a/dom/ipc/StructuredCloneUtils.cpp +++ b/dom/ipc/StructuredCloneUtils.cpp @@ -15,7 +15,6 @@ #include "nsJSEnvironment.h" #include "nsThreadUtils.h" #include "StructuredCloneTags.h" -#include "jsapi.h" using namespace mozilla::dom; diff --git a/dom/ipc/StructuredCloneUtils.h b/dom/ipc/StructuredCloneUtils.h index d403558661c9..9c285775b2db 100644 --- a/dom/ipc/StructuredCloneUtils.h +++ b/dom/ipc/StructuredCloneUtils.h @@ -11,7 +11,7 @@ #include "nsTArray.h" #include "nsIDOMFile.h" -#include "js/StructuredClone.h" +#include "jsapi.h" namespace mozilla { diff --git a/dom/workers/Events.h b/dom/workers/Events.h index 16575e56f5d4..cbd252306677 100644 --- a/dom/workers/Events.h +++ b/dom/workers/Events.h @@ -8,7 +8,7 @@ #include "Workers.h" -#include "js/StructuredClone.h" +class JSAutoStructuredCloneBuffer; BEGIN_WORKERS_NAMESPACE diff --git a/dom/workers/XMLHttpRequest.h b/dom/workers/XMLHttpRequest.h index 24239eacbb48..458813570ecc 100644 --- a/dom/workers/XMLHttpRequest.h +++ b/dom/workers/XMLHttpRequest.h @@ -15,8 +15,6 @@ #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/TypedArray.h" -#include "js/StructuredClone.h" - BEGIN_WORKERS_NAMESPACE class Proxy; diff --git a/ipc/glue/IPCMessageUtils.h b/ipc/glue/IPCMessageUtils.h index 83e267b25ed2..bbc1fb661160 100644 --- a/ipc/glue/IPCMessageUtils.h +++ b/ipc/glue/IPCMessageUtils.h @@ -22,7 +22,7 @@ #include "nsMemory.h" #include "nsStringGlue.h" #include "nsTArray.h" -#include "js/StructuredClone.h" +#include "jsapi.h" #include "nsCSSProperty.h" #ifdef _MSC_VER diff --git a/js/ipc/JavaScriptParent.h b/js/ipc/JavaScriptParent.h index afd18b6652e5..041149e3dde8 100644 --- a/js/ipc/JavaScriptParent.h +++ b/js/ipc/JavaScriptParent.h @@ -10,7 +10,6 @@ #include "JavaScriptShared.h" #include "mozilla/jsipc/PJavaScriptParent.h" -#include "jsclass.h" #ifdef XP_WIN #undef GetClassName diff --git a/js/public/ProfilingStack.h b/js/public/ProfilingStack.h deleted file mode 100644 index 36af5969034d..000000000000 --- a/js/public/ProfilingStack.h +++ /dev/null @@ -1,94 +0,0 @@ -/* -*- 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 js_ProfilingStack_h -#define js_ProfilingStack_h - -#include "jsbytecode.h" -#include "jstypes.h" - -#include "js/Utility.h" - -struct JSRuntime; -class JSScript; - -namespace js { - -// A call stack can be specified to the JS engine such that all JS entry/exits -// to functions push/pop an entry to/from the specified stack. -// -// For more detailed information, see vm/SPSProfiler.h. -// -class ProfileEntry -{ - // All fields are marked volatile to prevent the compiler from re-ordering - // instructions. Namely this sequence: - // - // 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 NULL 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 - // representation of the stack is stored such that all ProfileEntry - // instances are volatile. These methods would not be available unless they - // were marked as volatile as well. - - bool js() volatile { - JS_ASSERT_IF(sp == NULL, script_ != NULL); - return sp == NULL; - } - - uint32_t line() volatile { JS_ASSERT(!js()); return idx; } - JSScript *script() volatile { JS_ASSERT(js()); return script_; } - void *stackAddress() volatile { return sp; } - const char *label() volatile { return string; } - - void setLine(uint32_t aLine) volatile { JS_ASSERT(!js()); idx = aLine; } - void setLabel(const char *aString) volatile { string = aString; } - void setStackAddress(void *aSp) volatile { sp = aSp; } - void setScript(JSScript *aScript) volatile { script_ = aScript; } - - // We can't know the layout of JSScript, so look in vm/SPSProfiler.cpp. - JS_FRIEND_API(jsbytecode *) pc() volatile; - JS_FRIEND_API(void) setPC(jsbytecode *pc) volatile; - - static size_t offsetOfString() { return offsetof(ProfileEntry, string); } - static size_t offsetOfStackAddress() { return offsetof(ProfileEntry, sp); } - static size_t offsetOfPCIdx() { return offsetof(ProfileEntry, idx); } - static size_t offsetOfScript() { return offsetof(ProfileEntry, script_); } - - // The index used in the entry can either be a line number or the offset of - // a pc into a script's code. To signify a NULL pc, use a -1 index. This is - // checked against in pc() and setPC() to set/get the right pc. - static const int32_t NullPCIndex = -1; -}; - -JS_FRIEND_API(void) -SetRuntimeProfilingStack(JSRuntime *rt, ProfileEntry *stack, uint32_t *size, - uint32_t max); - -JS_FRIEND_API(void) -EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled); - -JS_FRIEND_API(jsbytecode*) -ProfilingGetPC(JSRuntime *rt, JSScript *script, void *ip); - -} // namespace js - -#endif /* js_ProfilingStack_h */ diff --git a/js/public/StructuredClone.h b/js/public/StructuredClone.h deleted file mode 100644 index 22084596e732..000000000000 --- a/js/public/StructuredClone.h +++ /dev/null @@ -1,162 +0,0 @@ -/* -*- 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 js_StructuredClone_h -#define js_StructuredClone_h - -#include - -#include "jstypes.h" - -struct JSContext; -class JSObject; -struct JSRuntime; -struct JSStructuredCloneReader; -struct JSStructuredCloneWriter; - -namespace JS { -template class Handle; -class Value; -} - -// API for the HTML5 internal structured cloning algorithm. - -// Read structured data from the reader r. This hook is used to read a value -// previously serialized by a call to the WriteStructuredCloneOp hook. -// -// tag and data are the pair of uint32_t values from the header. The callback -// may use the JS_Read* APIs to read any other relevant parts of the object -// from the reader r. closure is any value passed to the JS_ReadStructuredClone -// function. Return the new object on success, NULL on error/exception. -typedef JSObject *(*ReadStructuredCloneOp)(JSContext *cx, JSStructuredCloneReader *r, - uint32_t tag, uint32_t data, void *closure); - -// Structured data serialization hook. The engine can write primitive values, -// Objects, Arrays, Dates, RegExps, TypedArrays, and ArrayBuffers. Any other -// type of object requires application support. This callback must first use -// the JS_WriteUint32Pair API to write an object header, passing a value -// greater than JS_SCTAG_USER to the tag parameter. Then it can use the -// JS_Write* APIs to write any other relevant parts of the value v to the -// writer w. closure is any value passed to the JS_WriteStructuredCLone function. -// -// Return true on success, false on error/exception. -typedef bool (*WriteStructuredCloneOp)(JSContext *cx, JSStructuredCloneWriter *w, - JS::Handle obj, void *closure); - -// This is called when JS_WriteStructuredClone is given an invalid transferable. -// To follow HTML5, the application must throw a DATA_CLONE_ERR DOMException -// with error set to one of the JS_SCERR_* values. -typedef void (*StructuredCloneErrorOp)(JSContext *cx, uint32_t errorid); - -// The maximum supported structured-clone serialization format version. -#define JS_STRUCTURED_CLONE_VERSION 2 - -struct JSStructuredCloneCallbacks { - ReadStructuredCloneOp read; - WriteStructuredCloneOp write; - StructuredCloneErrorOp reportError; -}; - -// Note: if the *data contains transferable objects, it can be read only once. -JS_PUBLIC_API(bool) -JS_ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, uint32_t version, - JS::Value *vp, const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure); - -// Note: On success, the caller is responsible for calling -// JS_ClearStructuredClone(*datap, nbytesp). -JS_PUBLIC_API(bool) -JS_WriteStructuredClone(JSContext *cx, JS::Value v, uint64_t **datap, size_t *nbytesp, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure, JS::Value transferable); - -JS_PUBLIC_API(bool) -JS_ClearStructuredClone(const uint64_t *data, size_t nbytes); - -JS_PUBLIC_API(bool) -JS_StructuredCloneHasTransferables(const uint64_t *data, size_t nbytes, bool *hasTransferable); - -JS_PUBLIC_API(bool) -JS_StructuredClone(JSContext *cx, JS::Value v, JS::Value *vp, - const JSStructuredCloneCallbacks *optionalCallbacks, void *closure); - -// RAII sugar for JS_WriteStructuredClone. -class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) { - uint64_t *data_; - size_t nbytes_; - uint32_t version_; - - public: - JSAutoStructuredCloneBuffer() - : data_(NULL), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION) {} - - ~JSAutoStructuredCloneBuffer() { clear(); } - - uint64_t *data() const { return data_; } - size_t nbytes() const { return nbytes_; } - - void clear(); - - // Copy some memory. It will be automatically freed by the destructor. - bool copy(const uint64_t *data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION); - - // Adopt some memory. It will be automatically freed by the destructor. - // data must have been allocated by the JS engine (e.g., extracted via - // JSAutoStructuredCloneBuffer::steal). - void adopt(uint64_t *data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION); - - // Remove the buffer so that it will not be automatically freed. - // After this, the caller is responsible for feeding the memory back to - // JSAutoStructuredCloneBuffer::adopt. - void steal(uint64_t **datap, size_t *nbytesp, uint32_t *versionp=NULL); - - bool read(JSContext *cx, JS::Value *vp, - const JSStructuredCloneCallbacks *optionalCallbacks=NULL, void *closure=NULL); - - bool write(JSContext *cx, JS::Value v, - const JSStructuredCloneCallbacks *optionalCallbacks=NULL, void *closure=NULL); - - bool write(JSContext *cx, JS::Value v, JS::Value transferable, - const JSStructuredCloneCallbacks *optionalCallbacks=NULL, void *closure=NULL); - - // Swap ownership with another JSAutoStructuredCloneBuffer. - void swap(JSAutoStructuredCloneBuffer &other); - - private: - // Copy and assignment are not supported. - JSAutoStructuredCloneBuffer(const JSAutoStructuredCloneBuffer &other); - JSAutoStructuredCloneBuffer &operator=(const JSAutoStructuredCloneBuffer &other); -}; - -// The range of tag values the application may use for its own custom object types. -#define JS_SCTAG_USER_MIN ((uint32_t) 0xFFFF8000) -#define JS_SCTAG_USER_MAX ((uint32_t) 0xFFFFFFFF) - -#define JS_SCERR_RECURSION 0 -#define JS_SCERR_TRANSFERABLE 1 - -JS_PUBLIC_API(void) -JS_SetStructuredCloneCallbacks(JSRuntime *rt, const JSStructuredCloneCallbacks *callbacks); - -JS_PUBLIC_API(bool) -JS_ReadUint32Pair(JSStructuredCloneReader *r, uint32_t *p1, uint32_t *p2); - -JS_PUBLIC_API(bool) -JS_ReadBytes(JSStructuredCloneReader *r, void *p, size_t len); - -JS_PUBLIC_API(bool) -JS_ReadTypedArray(JSStructuredCloneReader *r, JS::Value *vp); - -JS_PUBLIC_API(bool) -JS_WriteUint32Pair(JSStructuredCloneWriter *w, uint32_t tag, uint32_t data); - -JS_PUBLIC_API(bool) -JS_WriteBytes(JSStructuredCloneWriter *w, const void *p, size_t len); - -JS_PUBLIC_API(bool) -JS_WriteTypedArray(JSStructuredCloneWriter *w, JS::Value v); - -#endif /* js_StructuredClone_h */ diff --git a/js/public/Value.h b/js/public/Value.h index 57ec128e9b29..e62ea8055912 100644 --- a/js/public/Value.h +++ b/js/public/Value.h @@ -1896,24 +1896,6 @@ JSVAL_TO_PRIVATE(jsval v) return JSVAL_TO_PRIVATE_PTR_IMPL(JSVAL_TO_IMPL(v)); } -// JS constants. For efficiency, prefer predicates (e.g. v.isNull()) and -// constructing values from scratch (e.g. Int32Value(0)). These constants are -// stored in memory and initialized at startup, so testing against them and -// using them requires memory loads and will be correspondingly slow. -extern JS_PUBLIC_DATA(const jsval) JSVAL_NULL; -extern JS_PUBLIC_DATA(const jsval) JSVAL_ZERO; -extern JS_PUBLIC_DATA(const jsval) JSVAL_ONE; -extern JS_PUBLIC_DATA(const jsval) JSVAL_FALSE; -extern JS_PUBLIC_DATA(const jsval) JSVAL_TRUE; -extern JS_PUBLIC_DATA(const jsval) JSVAL_VOID; - -namespace JS { - -extern JS_PUBLIC_DATA(const Handle) NullHandleValue; -extern JS_PUBLIC_DATA(const Handle) UndefinedHandleValue; - -} - #undef JS_VALUE_IS_CONSTEXPR #undef JS_RETURN_LAYOUT_FROM_BITS diff --git a/js/src/config/check_spidermonkey_style.py b/js/src/config/check_spidermonkey_style.py index accccdbdc560..93e9e8a68877 100644 --- a/js/src/config/check_spidermonkey_style.py +++ b/js/src/config/check_spidermonkey_style.py @@ -302,28 +302,6 @@ def module_name(name): return name.replace('inlines.h', '').replace('-inl.h', '').replace('.h', '').replace('.cpp', '') -def is_module_header(enclosing_inclname, header_inclname): - '''Determine if an included name is the "module header", i.e. should be - first in the file.''' - - module = module_name(enclosing_inclname) - - # Normal case, e.g. module == "foo/Bar", header_inclname == "foo/Bar.h". - if module == module_name(header_inclname): - return True - - # A public header, e.g. module == "foo/Bar", header_inclname == "js/Bar.h". - m = re.match(r'js\/(.*)\.h', header_inclname) - if m is not None and module.endswith('/' + m.group(1)): - return True - - # A weird public header case. - if module == 'jsmemorymetrics' and header_inclname == 'js/MemoryMetrics.h': - return True - - return False - - class Include(object): '''Important information for a single #include statement.''' @@ -335,7 +313,7 @@ class Include(object): def isLeaf(self): return True - def section(self, enclosing_inclname): + def section(self, module): '''Identify which section inclname belongs to. The section numbers are as follows. @@ -355,9 +333,11 @@ class Include(object): if not self.inclname.endswith('.h'): return 7 - # A couple of modules have the .h file in js/ and the .cpp file elsewhere and so need - # special handling. - if is_module_header(enclosing_inclname, self.inclname): + # A couple of modules have the .h file in js/ and the .cpp file elsewhere and so need special + # handling. + if module == module_name(self.inclname) or \ + module == 'jsmemorymetrics' and self.inclname == 'js/MemoryMetrics.h' or \ + module == 'vm/PropertyKey' and self.inclname == 'js/PropertyKey.h': return 0 if '/' in self.inclname: @@ -471,6 +451,8 @@ def do_file(filename, inclname, file_kind, f, all_inclnames, included_h_inclname if inclname == include.inclname: error(filename, include.linenum, 'the file includes itself') + module = module_name(inclname) + def check_includes_order(include1, include2): '''Check the ordering of two #include statements.''' @@ -478,8 +460,8 @@ def do_file(filename, inclname, file_kind, f, all_inclnames, included_h_inclname include2.inclname in oddly_ordered_inclnames: return - section1 = include1.section(inclname) - section2 = include2.section(inclname) + section1 = include1.section(module) + section2 = include2.section(module) if (section1 > section2) or \ ((section1 == section2) and (include1.inclname.lower() > include2.inclname.lower())): error(filename, str(include1.linenum) + ':' + str(include2.linenum), diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index b556031fb4cf..e667fdfe90cf 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -21,6 +21,7 @@ #include "jsarray.h" #include "jsatom.h" #include "jsbool.h" +#include "jsclone.h" #include "jscntxt.h" #include "jsdate.h" #include "jsexn.h" @@ -59,7 +60,6 @@ #include "gc/Marking.h" #include "jit/AsmJSLink.h" #include "js/CharacterEncoding.h" -#include "js/StructuredClone.h" #if ENABLE_INTL_API #include "unicode/uclean.h" #include "unicode/utypes.h" @@ -128,6 +128,17 @@ const jsid JSID_VOID = { size_t(JSID_TYPE_VOID) }; const jsid JSID_EMPTY = { size_t(JSID_TYPE_OBJECT) }; #endif +const jsval JSVAL_NULL = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_NULL, 0)); +const jsval JSVAL_ZERO = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_INT32, 0)); +const jsval JSVAL_ONE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_INT32, 1)); +const jsval JSVAL_FALSE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_BOOLEAN, false)); +const jsval JSVAL_TRUE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_BOOLEAN, true)); +const jsval JSVAL_VOID = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_UNDEFINED, 0)); +const HandleValue JS::NullHandleValue = + HandleValue::fromMarkedLocation(&JSVAL_NULL); +const HandleValue JS::UndefinedHandleValue = + HandleValue::fromMarkedLocation(&JSVAL_VOID); + const jsid voidIdValue = JSID_VOID; const jsid emptyIdValue = JSID_EMPTY; const HandleId JS::JSID_VOIDHANDLE = HandleId::fromMarkedLocation(&voidIdValue); @@ -174,22 +185,18 @@ JS_GetEmptyString(JSRuntime *rt) return rt->emptyString; } -namespace js { - -void +static void AssertHeapIsIdle(JSRuntime *rt) { JS_ASSERT(rt->heapState == js::Idle); } -void +static void AssertHeapIsIdle(JSContext *cx) { AssertHeapIsIdle(cx->runtime()); } -} - static void AssertHeapIsIdleOrIterating(JSRuntime *rt) { @@ -5918,6 +5925,220 @@ JS_ParseJSONWithReviver(JSContext *cx, const jschar *chars, uint32_t len, jsval return true; } +JS_PUBLIC_API(bool) +JS_ReadStructuredClone(JSContext *cx, uint64_t *buf, size_t nbytes, + uint32_t version, jsval *vp, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure) +{ + AssertHeapIsIdle(cx); + CHECK_REQUEST(cx); + + if (version > JS_STRUCTURED_CLONE_VERSION) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CLONE_VERSION); + return false; + } + const JSStructuredCloneCallbacks *callbacks = + optionalCallbacks ? + optionalCallbacks : + cx->runtime()->structuredCloneCallbacks; + return ReadStructuredClone(cx, buf, nbytes, vp, callbacks, closure); +} + +JS_PUBLIC_API(bool) +JS_WriteStructuredClone(JSContext *cx, jsval valueArg, uint64_t **bufp, size_t *nbytesp, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure, jsval transferable) +{ + RootedValue value(cx, valueArg); + AssertHeapIsIdle(cx); + CHECK_REQUEST(cx); + assertSameCompartment(cx, value); + + const JSStructuredCloneCallbacks *callbacks = + optionalCallbacks ? + optionalCallbacks : + cx->runtime()->structuredCloneCallbacks; + return WriteStructuredClone(cx, value, (uint64_t **) bufp, nbytesp, + callbacks, closure, transferable); +} + +JS_PUBLIC_API(bool) +JS_ClearStructuredClone(const uint64_t *data, size_t nbytes) +{ + return ClearStructuredClone(data, nbytes); +} + +JS_PUBLIC_API(bool) +JS_StructuredCloneHasTransferables(const uint64_t *data, size_t nbytes, + bool *hasTransferable) +{ + bool transferable; + if (!StructuredCloneHasTransferObjects(data, nbytes, &transferable)) + return false; + + *hasTransferable = transferable; + return true; +} + +JS_PUBLIC_API(bool) +JS_StructuredClone(JSContext *cx, jsval valueArg, jsval *vp, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure) +{ + RootedValue value(cx, valueArg); + AssertHeapIsIdle(cx); + CHECK_REQUEST(cx); + assertSameCompartment(cx, value); + + const JSStructuredCloneCallbacks *callbacks = + optionalCallbacks ? + optionalCallbacks : + cx->runtime()->structuredCloneCallbacks; + JSAutoStructuredCloneBuffer buf; + return buf.write(cx, value, callbacks, closure) && + buf.read(cx, vp, callbacks, closure); +} + +void +JSAutoStructuredCloneBuffer::clear() +{ + if (data_) { + ClearStructuredClone(data_, nbytes_); + data_ = NULL; + nbytes_ = 0; + version_ = 0; + } +} + +void +JSAutoStructuredCloneBuffer::adopt(uint64_t *data, size_t nbytes, uint32_t version) +{ + clear(); + data_ = data; + nbytes_ = nbytes; + version_ = version; +} + +bool +JSAutoStructuredCloneBuffer::copy(const uint64_t *srcData, size_t nbytes, uint32_t version) +{ + // transferable objects cannot be copied + bool hasTransferable; + if (!StructuredCloneHasTransferObjects(data_, nbytes_, &hasTransferable) || + hasTransferable) + return false; + + uint64_t *newData = static_cast(js_malloc(nbytes)); + if (!newData) + return false; + + js_memcpy(newData, srcData, nbytes); + + clear(); + data_ = newData; + nbytes_ = nbytes; + version_ = version; + return true; +} +void +JSAutoStructuredCloneBuffer::steal(uint64_t **datap, size_t *nbytesp, uint32_t *versionp) +{ + *datap = data_; + *nbytesp = nbytes_; + if (versionp) + *versionp = version_; + + data_ = NULL; + nbytes_ = 0; + version_ = 0; +} + +bool +JSAutoStructuredCloneBuffer::read(JSContext *cx, jsval *vp, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure) +{ + JS_ASSERT(cx); + JS_ASSERT(data_); + return !!JS_ReadStructuredClone(cx, data_, nbytes_, version_, vp, + optionalCallbacks, closure); +} + +bool +JSAutoStructuredCloneBuffer::write(JSContext *cx, jsval valueArg, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure) +{ + jsval transferable = JSVAL_VOID; + return write(cx, valueArg, transferable, optionalCallbacks, closure); +} + +bool +JSAutoStructuredCloneBuffer::write(JSContext *cx, jsval valueArg, + jsval transferable, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure) +{ + RootedValue value(cx, valueArg); + clear(); + bool ok = !!JS_WriteStructuredClone(cx, value, &data_, &nbytes_, + optionalCallbacks, closure, + transferable); + if (!ok) { + data_ = NULL; + nbytes_ = 0; + version_ = JS_STRUCTURED_CLONE_VERSION; + } + return ok; +} + +void +JSAutoStructuredCloneBuffer::swap(JSAutoStructuredCloneBuffer &other) +{ + uint64_t *data = other.data_; + size_t nbytes = other.nbytes_; + uint32_t version = other.version_; + + other.data_ = this->data_; + other.nbytes_ = this->nbytes_; + other.version_ = this->version_; + + this->data_ = data; + this->nbytes_ = nbytes; + this->version_ = version; +} + +JS_PUBLIC_API(void) +JS_SetStructuredCloneCallbacks(JSRuntime *rt, const JSStructuredCloneCallbacks *callbacks) +{ + rt->structuredCloneCallbacks = callbacks; +} + +JS_PUBLIC_API(bool) +JS_ReadUint32Pair(JSStructuredCloneReader *r, uint32_t *p1, uint32_t *p2) +{ + return r->input().readPair((uint32_t *) p1, (uint32_t *) p2); +} + +JS_PUBLIC_API(bool) +JS_ReadBytes(JSStructuredCloneReader *r, void *p, size_t len) +{ + return r->input().readBytes(p, len); +} + +JS_PUBLIC_API(bool) +JS_WriteUint32Pair(JSStructuredCloneWriter *w, uint32_t tag, uint32_t data) +{ + return w->output().writePair(tag, data); +} + +JS_PUBLIC_API(bool) +JS_WriteBytes(JSStructuredCloneWriter *w, const void *p, size_t len) +{ + return w->output().writeBytes(p, len); +} + /************************************************************************/ JS_PUBLIC_API(void) diff --git a/js/src/jsapi.h b/js/src/jsapi.h index b7e625aeca12..f1c9f61c7327 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1147,8 +1147,54 @@ typedef void (* JSCompartmentNameCallback)(JSRuntime *rt, JSCompartment *compartment, char *buf, size_t bufsize); +/* + * Read structured data from the reader r. This hook is used to read a value + * previously serialized by a call to the WriteStructuredCloneOp hook. + * + * tag and data are the pair of uint32_t values from the header. The callback + * may use the JS_Read* APIs to read any other relevant parts of the object + * from the reader r. closure is any value passed to the JS_ReadStructuredClone + * function. Return the new object on success, NULL on error/exception. + */ +typedef JSObject *(*ReadStructuredCloneOp)(JSContext *cx, JSStructuredCloneReader *r, + uint32_t tag, uint32_t data, void *closure); + +/* + * Structured data serialization hook. The engine can write primitive values, + * Objects, Arrays, Dates, RegExps, TypedArrays, and ArrayBuffers. Any other + * type of object requires application support. This callback must first use + * the JS_WriteUint32Pair API to write an object header, passing a value + * greater than JS_SCTAG_USER to the tag parameter. Then it can use the + * JS_Write* APIs to write any other relevant parts of the value v to the + * writer w. closure is any value passed to the JS_WriteStructuredCLone function. + * + * Return true on success, false on error/exception. + */ +typedef bool (*WriteStructuredCloneOp)(JSContext *cx, JSStructuredCloneWriter *w, + JS::Handle obj, void *closure); + +/* + * This is called when JS_WriteStructuredClone is given an invalid transferable. + * To follow HTML5, the application must throw a DATA_CLONE_ERR DOMException + * with error set to one of the JS_SCERR_* values. + */ +typedef void (*StructuredCloneErrorOp)(JSContext *cx, uint32_t errorid); + /************************************************************************/ +/* + * JS constants. For efficiency, prefer predicates (e.g. v.isNull()) and + * constructing values from scratch (e.g. Int32Value(0)). These constants are + * stored in memory and initialized at startup, so testing against them and + * using them requires memory loads and will be correspondingly slow. + */ +extern JS_PUBLIC_DATA(const jsval) JSVAL_NULL; +extern JS_PUBLIC_DATA(const jsval) JSVAL_ZERO; +extern JS_PUBLIC_DATA(const jsval) JSVAL_ONE; +extern JS_PUBLIC_DATA(const jsval) JSVAL_FALSE; +extern JS_PUBLIC_DATA(const jsval) JSVAL_TRUE; +extern JS_PUBLIC_DATA(const jsval) JSVAL_VOID; + static JS_ALWAYS_INLINE jsval JS_NumberValue(double d) { @@ -1830,12 +1876,6 @@ template <> struct GCMethods #endif }; -void -AssertHeapIsIdle(JSRuntime *rt); - -void -AssertHeapIsIdle(JSContext *cx); - } /* namespace js */ class JSAutoRequest @@ -4669,6 +4709,134 @@ JS_ParseJSONWithReviver(JSContext *cx, const jschar *chars, uint32_t len, jsval /************************************************************************/ +/* API for the HTML5 internal structured cloning algorithm. */ + +/* The maximum supported structured-clone serialization format version. */ +#define JS_STRUCTURED_CLONE_VERSION 2 + +struct JSStructuredCloneCallbacks { + ReadStructuredCloneOp read; + WriteStructuredCloneOp write; + StructuredCloneErrorOp reportError; +}; + +/* Note: if the *data contains transferable objects, it can be read + * only once */ +JS_PUBLIC_API(bool) +JS_ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, + uint32_t version, jsval *vp, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure); + +/* Note: On success, the caller is responsible for calling + * JS_ClearStructuredClone(*datap, nbytesp). */ +JS_PUBLIC_API(bool) +JS_WriteStructuredClone(JSContext *cx, jsval v, uint64_t **datap, size_t *nbytesp, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure, jsval transferable); + +JS_PUBLIC_API(bool) +JS_ClearStructuredClone(const uint64_t *data, size_t nbytes); + +JS_PUBLIC_API(bool) +JS_StructuredCloneHasTransferables(const uint64_t *data, size_t nbytes, + bool *hasTransferable); + +JS_PUBLIC_API(bool) +JS_StructuredClone(JSContext *cx, jsval v, jsval *vp, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure); + +/* RAII sugar for JS_WriteStructuredClone. */ +class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) { + uint64_t *data_; + size_t nbytes_; + uint32_t version_; + + public: + JSAutoStructuredCloneBuffer() + : data_(NULL), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION) {} + + ~JSAutoStructuredCloneBuffer() { clear(); } + + uint64_t *data() const { return data_; } + size_t nbytes() const { return nbytes_; } + + void clear(); + + /* Copy some memory. It will be automatically freed by the destructor. */ + bool copy(const uint64_t *data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION); + + /* + * Adopt some memory. It will be automatically freed by the destructor. + * data must have been allocated by the JS engine (e.g., extracted via + * JSAutoStructuredCloneBuffer::steal). + */ + void adopt(uint64_t *data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION); + + /* + * Remove the buffer so that it will not be automatically freed. + * After this, the caller is responsible for feeding the memory back to + * JSAutoStructuredCloneBuffer::adopt. + */ + void steal(uint64_t **datap, size_t *nbytesp, uint32_t *versionp=NULL); + + bool read(JSContext *cx, jsval *vp, + const JSStructuredCloneCallbacks *optionalCallbacks=NULL, + void *closure=NULL); + + bool write(JSContext *cx, jsval v, + const JSStructuredCloneCallbacks *optionalCallbacks=NULL, + void *closure=NULL); + + bool write(JSContext *cx, jsval v, + jsval transferable, + const JSStructuredCloneCallbacks *optionalCallbacks=NULL, + void *closure=NULL); + + /** + * Swap ownership with another JSAutoStructuredCloneBuffer. + */ + void swap(JSAutoStructuredCloneBuffer &other); + + private: + /* Copy and assignment are not supported. */ + JSAutoStructuredCloneBuffer(const JSAutoStructuredCloneBuffer &other); + JSAutoStructuredCloneBuffer &operator=(const JSAutoStructuredCloneBuffer &other); +}; + +/* API for implementing custom serialization behavior (for ImageData, File, etc.) */ + +/* The range of tag values the application may use for its own custom object types. */ +#define JS_SCTAG_USER_MIN ((uint32_t) 0xFFFF8000) +#define JS_SCTAG_USER_MAX ((uint32_t) 0xFFFFFFFF) + +#define JS_SCERR_RECURSION 0 +#define JS_SCERR_TRANSFERABLE 1 + +JS_PUBLIC_API(void) +JS_SetStructuredCloneCallbacks(JSRuntime *rt, const JSStructuredCloneCallbacks *callbacks); + +JS_PUBLIC_API(bool) +JS_ReadUint32Pair(JSStructuredCloneReader *r, uint32_t *p1, uint32_t *p2); + +JS_PUBLIC_API(bool) +JS_ReadBytes(JSStructuredCloneReader *r, void *p, size_t len); + +JS_PUBLIC_API(bool) +JS_ReadTypedArray(JSStructuredCloneReader *r, jsval *vp); + +JS_PUBLIC_API(bool) +JS_WriteUint32Pair(JSStructuredCloneWriter *w, uint32_t tag, uint32_t data); + +JS_PUBLIC_API(bool) +JS_WriteBytes(JSStructuredCloneWriter *w, const void *p, size_t len); + +JS_PUBLIC_API(bool) +JS_WriteTypedArray(JSStructuredCloneWriter *w, jsval v); + +/************************************************************************/ + /* * The default locale for the ECMAScript Internationalization API * (Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat). @@ -5087,6 +5255,9 @@ JS_DecodeInterpretedFunction(JSContext *cx, const void *data, uint32_t length, namespace JS { +extern JS_PUBLIC_DATA(const Handle) NullHandleValue; +extern JS_PUBLIC_DATA(const Handle) UndefinedHandleValue; + extern JS_PUBLIC_DATA(const Handle) JSID_VOIDHANDLE; extern JS_PUBLIC_DATA(const Handle) JSID_EMPTYHANDLE; diff --git a/js/src/vm/StructuredClone.cpp b/js/src/jsclone.cpp similarity index 75% rename from js/src/vm/StructuredClone.cpp rename to js/src/jsclone.cpp index c3e1dc33b808..dac5ca559883 100644 --- a/js/src/vm/StructuredClone.cpp +++ b/js/src/jsclone.cpp @@ -27,13 +27,11 @@ * array object. */ -#include "js/StructuredClone.h" +#include "jsclone.h" #include "mozilla/Endian.h" #include "mozilla/FloatingPoint.h" -#include "jsapi.h" -#include "jscntxt.h" #include "jsdate.h" #include "jswrapper.h" @@ -88,181 +86,6 @@ enum TransferableMapHeader { SCTAG_TM_MARKED }; -namespace js { - -struct SCOutput { - public: - explicit SCOutput(JSContext *cx); - - JSContext *context() const { return cx; } - - bool write(uint64_t u); - bool writePair(uint32_t tag, uint32_t data); - bool writeDouble(double d); - bool writeBytes(const void *p, size_t nbytes); - bool writeChars(const jschar *p, size_t nchars); - bool writePtr(const void *); - - template - bool writeArray(const T *p, size_t nbytes); - - bool extractBuffer(uint64_t **datap, size_t *sizep); - - uint64_t count() { return buf.length(); } - - private: - JSContext *cx; - js::Vector buf; -}; - -struct SCInput { - public: - SCInput(JSContext *cx, uint64_t *data, size_t nbytes); - - JSContext *context() const { return cx; } - - bool read(uint64_t *p); - bool readPair(uint32_t *tagp, uint32_t *datap); - bool readDouble(double *p); - bool readBytes(void *p, size_t nbytes); - bool readChars(jschar *p, size_t nchars); - bool readPtr(void **); - - bool get(uint64_t *p); - bool getPair(uint32_t *tagp, uint32_t *datap); - - bool replace(uint64_t u); - bool replacePair(uint32_t tag, uint32_t data); - - template - bool readArray(T *p, size_t nelems); - - private: - bool eof(); - - void staticAssertions() { - JS_STATIC_ASSERT(sizeof(jschar) == 2); - JS_STATIC_ASSERT(sizeof(uint32_t) == 4); - JS_STATIC_ASSERT(sizeof(double) == 8); - } - - JSContext *cx; - uint64_t *point; - uint64_t *end; -}; - -} - -struct JSStructuredCloneReader { - public: - explicit JSStructuredCloneReader(js::SCInput &in, const JSStructuredCloneCallbacks *cb, - void *cbClosure) - : in(in), objs(in.context()), allObjs(in.context()), - callbacks(cb), closure(cbClosure) { } - - js::SCInput &input() { return in; } - bool read(js::Value *vp); - - private: - JSContext *context() { return in.context(); } - - bool readTransferMap(); - - bool checkDouble(double d); - JSString *readString(uint32_t nchars); - bool readTypedArray(uint32_t arrayType, uint32_t nelems, js::Value *vp, bool v1Read = false); - bool readArrayBuffer(uint32_t nbytes, js::Value *vp); - bool readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems, js::Value *vp); - bool readId(jsid *idp); - bool startRead(js::Value *vp); - - js::SCInput ∈ - - // Stack of objects with properties remaining to be read. - js::AutoValueVector objs; - - // Stack of all objects read during this deserialization - js::AutoValueVector allObjs; - - // The user defined callbacks that will be used for cloning. - const JSStructuredCloneCallbacks *callbacks; - - // Any value passed to JS_ReadStructuredClone. - void *closure; - - friend bool JS_ReadTypedArray(JSStructuredCloneReader *r, JS::Value *vp); -}; - -struct JSStructuredCloneWriter { - public: - explicit JSStructuredCloneWriter(js::SCOutput &out, - const JSStructuredCloneCallbacks *cb, - void *cbClosure, - jsval tVal) - : out(out), objs(out.context()), - counts(out.context()), ids(out.context()), - memory(out.context()), callbacks(cb), closure(cbClosure), - transferable(out.context(), tVal), transferableObjects(out.context()) { } - - bool init() { return transferableObjects.init() && parseTransferable() && - memory.init() && writeTransferMap(); } - - bool write(const js::Value &v); - - js::SCOutput &output() { return out; } - - private: - JSContext *context() { return out.context(); } - - bool writeTransferMap(); - - bool writeString(uint32_t tag, JSString *str); - bool writeId(jsid id); - bool writeArrayBuffer(JS::HandleObject obj); - bool writeTypedArray(JS::HandleObject obj); - bool startObject(JS::HandleObject obj, bool *backref); - bool startWrite(const js::Value &v); - bool traverseObject(JS::HandleObject obj); - - bool parseTransferable(); - void reportErrorTransferable(); - - inline void checkStack(); - - js::SCOutput &out; - - // Vector of objects with properties remaining to be written. - // - // NB: These can span multiple compartments, so the compartment must be - // entered before any manipulation is performed. - js::AutoValueVector objs; - - // counts[i] is the number of properties of objs[i] remaining to be written. - // counts.length() == objs.length() and sum(counts) == ids.length(). - js::Vector counts; - - // Ids of properties remaining to be written. - js::AutoIdVector ids; - - // The "memory" list described in the HTML5 internal structured cloning algorithm. - // memory is a superset of objs; items are never removed from Memory - // until a serialization operation is finished - typedef js::AutoObjectUnsigned32HashMap CloneMemory; - CloneMemory memory; - - // The user defined callbacks that will be used for cloning. - const JSStructuredCloneCallbacks *callbacks; - - // Any value passed to JS_WriteStructuredClone. - void *closure; - - // List of transferable objects - JS::RootedValue transferable; - js::AutoObjectHashSet transferableObjects; - - friend bool JS_WriteTypedArray(JSStructuredCloneWriter *w, JS::Value v); -}; - JS_FRIEND_API(uint64_t) js_GetSCOffset(JSStructuredCloneWriter* writer) { @@ -274,12 +97,10 @@ JS_STATIC_ASSERT(SCTAG_END_OF_BUILTIN_TYPES <= JS_SCTAG_USER_MIN); JS_STATIC_ASSERT(JS_SCTAG_USER_MIN <= JS_SCTAG_USER_MAX); JS_STATIC_ASSERT(TypedArrayObject::TYPE_INT8 == 0); -namespace js { - bool -WriteStructuredClone(JSContext *cx, HandleValue v, uint64_t **bufp, size_t *nbytesp, - const JSStructuredCloneCallbacks *cb, void *cbClosure, - jsval transferable) +js::WriteStructuredClone(JSContext *cx, HandleValue v, uint64_t **bufp, size_t *nbytesp, + const JSStructuredCloneCallbacks *cb, void *cbClosure, + jsval transferable) { SCOutput out(cx); JSStructuredCloneWriter w(out, cb, cbClosure, transferable); @@ -287,8 +108,8 @@ WriteStructuredClone(JSContext *cx, HandleValue v, uint64_t **bufp, size_t *nbyt } bool -ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, Value *vp, - const JSStructuredCloneCallbacks *cb, void *cbClosure) +js::ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, Value *vp, + const JSStructuredCloneCallbacks *cb, void *cbClosure) { SCInput in(cx, data, nbytes); @@ -300,7 +121,7 @@ ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, Value *vp, } bool -ClearStructuredClone(const uint64_t *data, size_t nbytes) +js::ClearStructuredClone(const uint64_t *data, size_t nbytes) { const uint64_t *point = data; const uint64_t *end = data + nbytes / 8; @@ -331,7 +152,7 @@ ClearStructuredClone(const uint64_t *data, size_t nbytes) } bool -StructuredCloneHasTransferObjects(const uint64_t *data, size_t nbytes, bool *hasTransferable) +js::StructuredCloneHasTransferObjects(const uint64_t *data, size_t nbytes, bool *hasTransferable) { *hasTransferable = false; @@ -346,8 +167,6 @@ StructuredCloneHasTransferObjects(const uint64_t *data, size_t nbytes, bool *has return true; } -} - static inline uint64_t PairToUInt64(uint32_t tag, uint32_t data) { @@ -742,6 +561,24 @@ JSStructuredCloneWriter::checkStack() #endif } +JS_PUBLIC_API(bool) +JS_WriteTypedArray(JSStructuredCloneWriter *w, jsval v) +{ + JS_ASSERT(v.isObject()); + assertSameCompartment(w->context(), v); + RootedObject obj(w->context(), &v.toObject()); + + // If the object is a security wrapper, see if we're allowed to unwrap it. + // If we aren't, throw. + if (obj->is()) + obj = CheckedUnwrap(obj); + if (!obj) { + JS_ReportError(w->context(), "Permission denied to access object"); + return false; + } + return w->writeTypedArray(obj); +} + /* * Write out a typed array. Note that post-v1 structured clone buffers do not * perform endianness conversion on stored data, so multibyte typed arrays @@ -1015,6 +852,26 @@ TagToV1ArrayType(uint32_t tag) return tag - SCTAG_TYPED_ARRAY_V1_MIN; } +JS_PUBLIC_API(bool) +JS_ReadTypedArray(JSStructuredCloneReader *r, jsval *vp) +{ + uint32_t tag, nelems; + if (!r->input().readPair(&tag, &nelems)) + return false; + if (tag >= SCTAG_TYPED_ARRAY_V1_MIN && tag <= SCTAG_TYPED_ARRAY_V1_MAX) { + return r->readTypedArray(TagToV1ArrayType(tag), nelems, vp, true); + } else if (tag == SCTAG_TYPED_ARRAY_OBJECT) { + uint64_t arrayType; + if (!r->input().read(&arrayType)) + return false; + return r->readTypedArray(arrayType, nelems, vp); + } else { + JS_ReportErrorNumber(r->context(), js_GetErrorMessage, NULL, + JSMSG_SC_BAD_SERIALIZED_DATA, "expected type array"); + return false; + } +} + bool JSStructuredCloneReader::readTypedArray(uint32_t arrayType, uint32_t nelems, Value *vp, bool v1Read) @@ -1423,257 +1280,3 @@ JSStructuredCloneReader::read(Value *vp) return true; } -using namespace js; - -JS_PUBLIC_API(bool) -JS_ReadStructuredClone(JSContext *cx, uint64_t *buf, size_t nbytes, - uint32_t version, JS::Value *vp, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure) -{ - AssertHeapIsIdle(cx); - CHECK_REQUEST(cx); - - if (version > JS_STRUCTURED_CLONE_VERSION) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CLONE_VERSION); - return false; - } - const JSStructuredCloneCallbacks *callbacks = - optionalCallbacks ? - optionalCallbacks : - cx->runtime()->structuredCloneCallbacks; - return ReadStructuredClone(cx, buf, nbytes, vp, callbacks, closure); -} - -JS_PUBLIC_API(bool) -JS_WriteStructuredClone(JSContext *cx, JS::Value valueArg, uint64_t **bufp, size_t *nbytesp, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure, JS::Value transferable) -{ - RootedValue value(cx, valueArg); - AssertHeapIsIdle(cx); - CHECK_REQUEST(cx); - assertSameCompartment(cx, value); - - const JSStructuredCloneCallbacks *callbacks = - optionalCallbacks ? - optionalCallbacks : - cx->runtime()->structuredCloneCallbacks; - return WriteStructuredClone(cx, value, bufp, nbytesp, callbacks, closure, transferable); -} - -JS_PUBLIC_API(bool) -JS_ClearStructuredClone(const uint64_t *data, size_t nbytes) -{ - return ClearStructuredClone(data, nbytes); -} - -JS_PUBLIC_API(bool) -JS_StructuredCloneHasTransferables(const uint64_t *data, size_t nbytes, - bool *hasTransferable) -{ - bool transferable; - if (!StructuredCloneHasTransferObjects(data, nbytes, &transferable)) - return false; - - *hasTransferable = transferable; - return true; -} - -JS_PUBLIC_API(bool) -JS_StructuredClone(JSContext *cx, JS::Value valueArg, JS::Value *vp, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure) -{ - RootedValue value(cx, valueArg); - AssertHeapIsIdle(cx); - CHECK_REQUEST(cx); - assertSameCompartment(cx, value); - - const JSStructuredCloneCallbacks *callbacks = - optionalCallbacks ? - optionalCallbacks : - cx->runtime()->structuredCloneCallbacks; - JSAutoStructuredCloneBuffer buf; - return buf.write(cx, value, callbacks, closure) && - buf.read(cx, vp, callbacks, closure); -} - -void -JSAutoStructuredCloneBuffer::clear() -{ - if (data_) { - ClearStructuredClone(data_, nbytes_); - data_ = NULL; - nbytes_ = 0; - version_ = 0; - } -} - -bool -JSAutoStructuredCloneBuffer::copy(const uint64_t *srcData, size_t nbytes, uint32_t version) -{ - // transferable objects cannot be copied - bool hasTransferable; - if (!StructuredCloneHasTransferObjects(data_, nbytes_, &hasTransferable) || - hasTransferable) - return false; - - uint64_t *newData = static_cast(js_malloc(nbytes)); - if (!newData) - return false; - - js_memcpy(newData, srcData, nbytes); - - clear(); - data_ = newData; - nbytes_ = nbytes; - version_ = version; - return true; -} - -void -JSAutoStructuredCloneBuffer::adopt(uint64_t *data, size_t nbytes, uint32_t version) -{ - clear(); - data_ = data; - nbytes_ = nbytes; - version_ = version; -} - -void -JSAutoStructuredCloneBuffer::steal(uint64_t **datap, size_t *nbytesp, uint32_t *versionp) -{ - *datap = data_; - *nbytesp = nbytes_; - if (versionp) - *versionp = version_; - - data_ = NULL; - nbytes_ = 0; - version_ = 0; -} - -bool -JSAutoStructuredCloneBuffer::read(JSContext *cx, JS::Value *vp, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure) -{ - JS_ASSERT(cx); - JS_ASSERT(data_); - return !!JS_ReadStructuredClone(cx, data_, nbytes_, version_, vp, - optionalCallbacks, closure); -} - -bool -JSAutoStructuredCloneBuffer::write(JSContext *cx, JS::Value valueArg, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure) -{ - JS::Value transferable = JSVAL_VOID; - return write(cx, valueArg, transferable, optionalCallbacks, closure); -} - -bool -JSAutoStructuredCloneBuffer::write(JSContext *cx, JS::Value valueArg, - JS::Value transferable, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure) -{ - RootedValue value(cx, valueArg); - clear(); - bool ok = !!JS_WriteStructuredClone(cx, value, &data_, &nbytes_, - optionalCallbacks, closure, - transferable); - if (!ok) { - data_ = NULL; - nbytes_ = 0; - version_ = JS_STRUCTURED_CLONE_VERSION; - } - return ok; -} - -void -JSAutoStructuredCloneBuffer::swap(JSAutoStructuredCloneBuffer &other) -{ - uint64_t *data = other.data_; - size_t nbytes = other.nbytes_; - uint32_t version = other.version_; - - other.data_ = this->data_; - other.nbytes_ = this->nbytes_; - other.version_ = this->version_; - - this->data_ = data; - this->nbytes_ = nbytes; - this->version_ = version; -} - -JS_PUBLIC_API(void) -JS_SetStructuredCloneCallbacks(JSRuntime *rt, const JSStructuredCloneCallbacks *callbacks) -{ - rt->structuredCloneCallbacks = callbacks; -} - -JS_PUBLIC_API(bool) -JS_ReadUint32Pair(JSStructuredCloneReader *r, uint32_t *p1, uint32_t *p2) -{ - return r->input().readPair((uint32_t *) p1, (uint32_t *) p2); -} - -JS_PUBLIC_API(bool) -JS_ReadBytes(JSStructuredCloneReader *r, void *p, size_t len) -{ - return r->input().readBytes(p, len); -} - -JS_PUBLIC_API(bool) -JS_ReadTypedArray(JSStructuredCloneReader *r, JS::Value *vp) -{ - uint32_t tag, nelems; - if (!r->input().readPair(&tag, &nelems)) - return false; - if (tag >= SCTAG_TYPED_ARRAY_V1_MIN && tag <= SCTAG_TYPED_ARRAY_V1_MAX) { - return r->readTypedArray(TagToV1ArrayType(tag), nelems, vp, true); - } else if (tag == SCTAG_TYPED_ARRAY_OBJECT) { - uint64_t arrayType; - if (!r->input().read(&arrayType)) - return false; - return r->readTypedArray(arrayType, nelems, vp); - } else { - JS_ReportErrorNumber(r->context(), js_GetErrorMessage, NULL, - JSMSG_SC_BAD_SERIALIZED_DATA, "expected type array"); - return false; - } -} - -JS_PUBLIC_API(bool) -JS_WriteUint32Pair(JSStructuredCloneWriter *w, uint32_t tag, uint32_t data) -{ - return w->output().writePair(tag, data); -} - -JS_PUBLIC_API(bool) -JS_WriteBytes(JSStructuredCloneWriter *w, const void *p, size_t len) -{ - return w->output().writeBytes(p, len); -} - -JS_PUBLIC_API(bool) -JS_WriteTypedArray(JSStructuredCloneWriter *w, JS::Value v) -{ - JS_ASSERT(v.isObject()); - assertSameCompartment(w->context(), v); - RootedObject obj(w->context(), &v.toObject()); - - // If the object is a security wrapper, see if we're allowed to unwrap it. - // If we aren't, throw. - if (obj->is()) - obj = CheckedUnwrap(obj); - if (!obj) { - JS_ReportError(w->context(), "Permission denied to access object"); - return false; - } - return w->writeTypedArray(obj); -} - diff --git a/js/src/jsclone.h b/js/src/jsclone.h new file mode 100644 index 000000000000..daec0e86bfe6 --- /dev/null +++ b/js/src/jsclone.h @@ -0,0 +1,206 @@ +/* -*- 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 jsclone_h +#define jsclone_h + +#include "jsapi.h" +#include "jscntxt.h" + +#include "js/Vector.h" + +namespace js { + +bool +WriteStructuredClone(JSContext *cx, HandleValue v, uint64_t **bufp, size_t *nbytesp, + const JSStructuredCloneCallbacks *cb, void *cbClosure, + jsval transferable); + +bool +ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, Value *vp, + const JSStructuredCloneCallbacks *cb, void *cbClosure); + +bool +ClearStructuredClone(const uint64_t *data, size_t nbytes); + +bool +StructuredCloneHasTransferObjects(const uint64_t *data, size_t nbytes, + bool *hasTransferable); + +struct SCOutput { + public: + explicit SCOutput(JSContext *cx); + + JSContext *context() const { return cx; } + + bool write(uint64_t u); + bool writePair(uint32_t tag, uint32_t data); + bool writeDouble(double d); + bool writeBytes(const void *p, size_t nbytes); + bool writeChars(const jschar *p, size_t nchars); + bool writePtr(const void *); + + template + bool writeArray(const T *p, size_t nbytes); + + bool extractBuffer(uint64_t **datap, size_t *sizep); + + uint64_t count() { return buf.length(); } + + private: + JSContext *cx; + js::Vector buf; +}; + +struct SCInput { + public: + SCInput(JSContext *cx, uint64_t *data, size_t nbytes); + + JSContext *context() const { return cx; } + + bool read(uint64_t *p); + bool readPair(uint32_t *tagp, uint32_t *datap); + bool readDouble(double *p); + bool readBytes(void *p, size_t nbytes); + bool readChars(jschar *p, size_t nchars); + bool readPtr(void **); + + bool get(uint64_t *p); + bool getPair(uint32_t *tagp, uint32_t *datap); + + bool replace(uint64_t u); + bool replacePair(uint32_t tag, uint32_t data); + + template + bool readArray(T *p, size_t nelems); + + private: + bool eof(); + + void staticAssertions() { + JS_STATIC_ASSERT(sizeof(jschar) == 2); + JS_STATIC_ASSERT(sizeof(uint32_t) == 4); + JS_STATIC_ASSERT(sizeof(double) == 8); + } + + JSContext *cx; + uint64_t *point; + uint64_t *end; +}; + +} /* namespace js */ + +struct JSStructuredCloneReader { + public: + explicit JSStructuredCloneReader(js::SCInput &in, const JSStructuredCloneCallbacks *cb, + void *cbClosure) + : in(in), objs(in.context()), allObjs(in.context()), + callbacks(cb), closure(cbClosure) { } + + js::SCInput &input() { return in; } + bool read(js::Value *vp); + + private: + JSContext *context() { return in.context(); } + + bool readTransferMap(); + + bool checkDouble(double d); + JSString *readString(uint32_t nchars); + bool readTypedArray(uint32_t arrayType, uint32_t nelems, js::Value *vp, bool v1Read = false); + bool readArrayBuffer(uint32_t nbytes, js::Value *vp); + bool readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems, js::Value *vp); + bool readId(jsid *idp); + bool startRead(js::Value *vp); + + js::SCInput ∈ + + // Stack of objects with properties remaining to be read. + js::AutoValueVector objs; + + // Stack of all objects read during this deserialization + js::AutoValueVector allObjs; + + // The user defined callbacks that will be used for cloning. + const JSStructuredCloneCallbacks *callbacks; + + // Any value passed to JS_ReadStructuredClone. + void *closure; + + friend bool JS_ReadTypedArray(JSStructuredCloneReader *r, jsval *vp); +}; + +struct JSStructuredCloneWriter { + public: + explicit JSStructuredCloneWriter(js::SCOutput &out, + const JSStructuredCloneCallbacks *cb, + void *cbClosure, + jsval tVal) + : out(out), objs(out.context()), + counts(out.context()), ids(out.context()), + memory(out.context()), callbacks(cb), closure(cbClosure), + transferable(out.context(), tVal), transferableObjects(out.context()) { } + + bool init() { return transferableObjects.init() && parseTransferable() && + memory.init() && writeTransferMap(); } + + bool write(const js::Value &v); + + js::SCOutput &output() { return out; } + + private: + JSContext *context() { return out.context(); } + + bool writeTransferMap(); + + bool writeString(uint32_t tag, JSString *str); + bool writeId(jsid id); + bool writeArrayBuffer(JS::HandleObject obj); + bool writeTypedArray(JS::HandleObject obj); + bool startObject(JS::HandleObject obj, bool *backref); + bool startWrite(const js::Value &v); + bool traverseObject(JS::HandleObject obj); + + bool parseTransferable(); + void reportErrorTransferable(); + + inline void checkStack(); + + js::SCOutput &out; + + // Vector of objects with properties remaining to be written. + // + // NB: These can span multiple compartments, so the compartment must be + // entered before any manipulation is performed. + js::AutoValueVector objs; + + // counts[i] is the number of properties of objs[i] remaining to be written. + // counts.length() == objs.length() and sum(counts) == ids.length(). + js::Vector counts; + + // Ids of properties remaining to be written. + js::AutoIdVector ids; + + // The "memory" list described in the HTML5 internal structured cloning algorithm. + // memory is a superset of objs; items are never removed from Memory + // until a serialization operation is finished + typedef js::AutoObjectUnsigned32HashMap CloneMemory; + CloneMemory memory; + + // The user defined callbacks that will be used for cloning. + const JSStructuredCloneCallbacks *callbacks; + + // Any value passed to JS_WriteStructuredClone. + void *closure; + + // List of transferable objects + JS::RootedValue transferable; + js::AutoObjectHashSet transferableObjects; + + friend bool JS_WriteTypedArray(JSStructuredCloneWriter *w, jsval v); +}; + +#endif /* jsclone_h */ diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index ef25ca501246..a85fa6355664 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -991,6 +991,24 @@ js::GetEnterCompartmentDepth(JSContext *cx) } #endif +JS_FRIEND_API(void) +js::SetRuntimeProfilingStack(JSRuntime *rt, ProfileEntry *stack, uint32_t *size, uint32_t max) +{ + rt->spsProfiler.setProfilingStack(stack, size, max); +} + +JS_FRIEND_API(void) +js::EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled) +{ + rt->spsProfiler.enable(enabled); +} + +JS_FRIEND_API(jsbytecode*) +js::ProfilingGetPC(JSRuntime *rt, JSScript *script, void *ip) +{ + return rt->spsProfiler.ipToPC(script, size_t(ip)); +} + JS_FRIEND_API(void) js::SetDOMCallbacks(JSRuntime *rt, const DOMCallbacks *callbacks) { diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index b76c3cf36319..b2b377880235 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -669,6 +669,84 @@ GetPCCountScriptSummary(JSContext *cx, size_t script); JS_FRIEND_API(JSString *) GetPCCountScriptContents(JSContext *cx, size_t script); +/* + * A call stack can be specified to the JS engine such that all JS entry/exits + * to functions push/pop an entry to/from the specified stack. + * + * For more detailed information, see vm/SPSProfiler.h + */ +class ProfileEntry +{ + /* + * All fields are marked volatile to prevent the compiler from re-ordering + * instructions. Namely this sequence: + * + * 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 NULL 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 + * representation of the stack is stored such that all ProfileEntry + * instances are volatile. These methods would not be available unless they + * were marked as volatile as well + */ + + bool js() volatile { + JS_ASSERT_IF(sp == NULL, script_ != NULL); + return sp == NULL; + } + + uint32_t line() volatile { JS_ASSERT(!js()); return idx; } + JSScript *script() volatile { JS_ASSERT(js()); return script_; } + void *stackAddress() volatile { return sp; } + const char *label() volatile { return string; } + + void setLine(uint32_t aLine) volatile { JS_ASSERT(!js()); idx = aLine; } + void setLabel(const char *aString) volatile { string = aString; } + void setStackAddress(void *aSp) volatile { sp = aSp; } + void setScript(JSScript *aScript) volatile { script_ = aScript; } + + /* we can't know the layout of JSScript, so look in vm/SPSProfiler.cpp */ + JS_FRIEND_API(jsbytecode *) pc() volatile; + JS_FRIEND_API(void) setPC(jsbytecode *pc) volatile; + + static size_t offsetOfString() { return offsetof(ProfileEntry, string); } + static size_t offsetOfStackAddress() { return offsetof(ProfileEntry, sp); } + static size_t offsetOfPCIdx() { return offsetof(ProfileEntry, idx); } + static size_t offsetOfScript() { return offsetof(ProfileEntry, script_); } + + /* + * The index used in the entry can either be a line number or the offset of + * a pc into a script's code. To signify a NULL pc, use a -1 index. This is + * checked against in pc() and setPC() to set/get the right pc. + */ + static const int32_t NullPCIndex = -1; +}; + +JS_FRIEND_API(void) +SetRuntimeProfilingStack(JSRuntime *rt, ProfileEntry *stack, uint32_t *size, + uint32_t max); + +JS_FRIEND_API(void) +EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled); + +JS_FRIEND_API(jsbytecode*) +ProfilingGetPC(JSRuntime *rt, JSScript *script, void *ip); + #ifdef JS_THREADSAFE JS_FRIEND_API(bool) ContextHasOutstandingRequests(const JSContext *cx); diff --git a/js/src/moz.build b/js/src/moz.build index 87ee3b53a51a..e380dc07b419 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -68,11 +68,9 @@ EXPORTS.js += [ '../public/HeapAPI.h', '../public/LegacyIntTypes.h', '../public/MemoryMetrics.h', - '../public/ProfilingStack.h', '../public/PropertyKey.h', '../public/RequiredDefines.h', '../public/RootingAPI.h', - '../public/StructuredClone.h', '../public/Utility.h', '../public/Value.h', '../public/Vector.h', @@ -127,13 +125,11 @@ CPP_SOURCES += [ 'StoreBuffer.cpp', 'String.cpp', 'StringBuffer.cpp', - 'StructuredClone.cpp', 'TestingFunctions.cpp', 'ThreadPool.cpp', 'TokenStream.cpp', 'TypedArrayObject.cpp', 'Unicode.cpp', - 'Value.cpp', 'Verifier.cpp', 'Xdr.cpp', 'YarrCanonicalizeUCS2.cpp', @@ -147,6 +143,7 @@ CPP_SOURCES += [ 'jsarray.cpp', 'jsatom.cpp', 'jsbool.cpp', + 'jsclone.cpp', 'jscntxt.cpp', 'jscompartment.cpp', 'jscrashreport.cpp', diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index d9267463fffa..d2eb07399b33 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -62,7 +62,6 @@ #include "frontend/BytecodeEmitter.h" #include "frontend/Parser.h" #include "jit/Ion.h" -#include "js/StructuredClone.h" #include "perf/jsperf.h" #include "shell/jsheaptools.h" #include "shell/jsoptparse.h" diff --git a/js/src/vm/CharacterEncoding.cpp b/js/src/vm/CharacterEncoding.cpp index 6e7a7f4ad17f..222caf14c08b 100644 --- a/js/src/vm/CharacterEncoding.cpp +++ b/js/src/vm/CharacterEncoding.cpp @@ -4,11 +4,11 @@ * 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 "js/CharacterEncoding.h" - #include "jscntxt.h" #include "jsprf.h" +#include "js/CharacterEncoding.h" + using namespace JS; Latin1CharsZ diff --git a/js/src/vm/SPSProfiler.cpp b/js/src/vm/SPSProfiler.cpp index 4dffb3976b33..4d3723e92ff1 100644 --- a/js/src/vm/SPSProfiler.cpp +++ b/js/src/vm/SPSProfiler.cpp @@ -265,33 +265,14 @@ SPSEntryMarker::~SPSEntryMarker() } JS_FRIEND_API(jsbytecode*) -ProfileEntry::pc() volatile -{ +ProfileEntry::pc() volatile { JS_ASSERT_IF(idx != NullPCIndex, idx >= 0 && uint32_t(idx) < script()->length); return idx == NullPCIndex ? NULL : script()->code + idx; } JS_FRIEND_API(void) -ProfileEntry::setPC(jsbytecode *pc) volatile -{ - JS_ASSERT_IF(pc != NULL, script()->code <= pc && pc < script()->code + script()->length); +ProfileEntry::setPC(jsbytecode *pc) volatile { + JS_ASSERT_IF(pc != NULL, script()->code <= pc && + pc < script()->code + script()->length); idx = pc == NULL ? NullPCIndex : pc - script()->code; } - -JS_FRIEND_API(void) -js::SetRuntimeProfilingStack(JSRuntime *rt, ProfileEntry *stack, uint32_t *size, uint32_t max) -{ - rt->spsProfiler.setProfilingStack(stack, size, max); -} - -JS_FRIEND_API(void) -js::EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled) -{ - rt->spsProfiler.enable(enabled); -} - -JS_FRIEND_API(jsbytecode*) -js::ProfilingGetPC(JSRuntime *rt, JSScript *script, void *ip) -{ - return rt->spsProfiler.ipToPC(script, size_t(ip)); -} diff --git a/js/src/vm/SPSProfiler.h b/js/src/vm/SPSProfiler.h index 224cd558da3a..c8a1e345789e 100644 --- a/js/src/vm/SPSProfiler.h +++ b/js/src/vm/SPSProfiler.h @@ -14,8 +14,6 @@ #include "jsscript.h" -#include "js/ProfilingStack.h" - /* * SPS Profiler integration with the JS Engine * https://developer.mozilla.org/en/Performance/Profiling_with_the_Built-in_Profiler diff --git a/js/src/vm/Value.cpp b/js/src/vm/Value.cpp deleted file mode 100644 index d88760b07600..000000000000 --- a/js/src/vm/Value.cpp +++ /dev/null @@ -1,21 +0,0 @@ -/* -*- 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 "js/Value.h" - -const jsval JSVAL_NULL = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_NULL, 0)); -const jsval JSVAL_ZERO = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_INT32, 0)); -const jsval JSVAL_ONE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_INT32, 1)); -const jsval JSVAL_FALSE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_BOOLEAN, false)); -const jsval JSVAL_TRUE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_BOOLEAN, true)); -const jsval JSVAL_VOID = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_UNDEFINED, 0)); - -namespace JS { - -const HandleValue NullHandleValue = HandleValue::fromMarkedLocation(&JSVAL_NULL); -const HandleValue UndefinedHandleValue = HandleValue::fromMarkedLocation(&JSVAL_VOID); - -} // namespace JS diff --git a/js/xpconnect/public/nsAutoJSValHolder.h b/js/xpconnect/public/nsAutoJSValHolder.h index 4a737f33eee3..54fe8f3be880 100644 --- a/js/xpconnect/public/nsAutoJSValHolder.h +++ b/js/xpconnect/public/nsAutoJSValHolder.h @@ -7,10 +7,9 @@ #define __NSAUTOJSVALHOLDER_H__ #include "nsDebug.h" -#include "jsapi.h" /** - * Simple class that looks and acts like a JS::Value except that it unroots + * Simple class that looks and acts like a jsval except that it unroots * itself automatically if Root() is ever called. Designed to be rooted on the * context or runtime (but not both!). */ @@ -45,7 +44,7 @@ public: else { this->Release(); } - *this = static_cast(aOther); + *this = static_cast(aOther); } return *this; } @@ -77,10 +76,10 @@ public: /** * Manually release, nullifying mVal, and mRt, but returning - * the original JS::Value. + * the original jsval. */ - JS::Value Release() { - JS::Value oldval = mVal; + jsval Release() { + jsval oldval = mVal; if (mRt) { JS_RemoveValueRootRT(mRt, &mVal); // infallible @@ -108,20 +107,20 @@ public: : nullptr; } - JS::Value* ToJSValPtr() { + jsval* ToJSValPtr() { return &mVal; } /** - * Pretend to be a JS::Value. + * Pretend to be a jsval. */ - operator JS::Value() const { return mVal; } + operator jsval() const { return mVal; } nsAutoJSValHolder &operator=(JSObject* aOther) { return *this = OBJECT_TO_JSVAL(aOther); } - nsAutoJSValHolder &operator=(JS::Value aOther) { + nsAutoJSValHolder &operator=(jsval aOther) { #ifdef DEBUG if (JSVAL_IS_GCTHING(aOther) && !JSVAL_IS_NULL(aOther)) { MOZ_ASSERT(IsHeld(), "Not rooted!"); @@ -132,7 +131,7 @@ public: } private: - JS::Value mVal; + jsval mVal; JSRuntime* mRt; }; diff --git a/security/manager/ssl/src/nsNSSIOLayer.cpp b/security/manager/ssl/src/nsNSSIOLayer.cpp index 41337b9985dd..6c42053ac1ad 100644 --- a/security/manager/ssl/src/nsNSSIOLayer.cpp +++ b/security/manager/ssl/src/nsNSSIOLayer.cpp @@ -7,7 +7,6 @@ #include "nsNSSComponent.h" #include "nsNSSIOLayer.h" -#include "mozilla/Casting.h" #include "mozilla/DebugOnly.h" #include "mozilla/Telemetry.h" diff --git a/tools/profiler/PseudoStack.h b/tools/profiler/PseudoStack.h index 66549bfec70c..a59eb9937b01 100644 --- a/tools/profiler/PseudoStack.h +++ b/tools/profiler/PseudoStack.h @@ -8,7 +8,7 @@ #include "mozilla/NullPtr.h" #include -#include "js/ProfilingStack.h" +#include "jsfriendapi.h" #include #include From f93c9bdeb0f167a3306121655677abba01ac14ed Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Thu, 22 Aug 2013 02:13:50 +0300 Subject: [PATCH 27/54] Bug 822096, make sure to not favor perf mode too long, r=roc --HG-- extra : rebase_source : 79ae8ff0f2ad0b4d5ce07895efdcc316e0264fc4 --- widget/xpwidgets/nsBaseAppShell.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/widget/xpwidgets/nsBaseAppShell.cpp b/widget/xpwidgets/nsBaseAppShell.cpp index d7573dfc0831..e83234febecf 100644 --- a/widget/xpwidgets/nsBaseAppShell.cpp +++ b/widget/xpwidgets/nsBaseAppShell.cpp @@ -181,11 +181,11 @@ nsBaseAppShell::FavorPerformanceHint(bool favorPerfOverStarvation, uint32_t starvationDelay) { mStarvationDelay = PR_MillisecondsToInterval(starvationDelay); + mSwitchTime = PR_IntervalNow(); if (favorPerfOverStarvation) { ++mFavorPerf; } else { --mFavorPerf; - mSwitchTime = PR_IntervalNow(); } return NS_OK; } @@ -268,7 +268,8 @@ nsBaseAppShell::OnProcessNextEvent(nsIThreadInternal *thr, bool mayWait, // NativeEventCallback to process gecko events. mProcessedGeckoEvents = false; - if (mFavorPerf <= 0 && start > mSwitchTime + mStarvationDelay) { + if (mFavorPerf <= 0 || + start > mSwitchTime + std::max(mStarvationDelay, limit)) { // Favor pending native events PRIntervalTime now = start; bool keepGoing; From ae52eee957feba78e196c66295aa8f730f1bc944 Mon Sep 17 00:00:00 2001 From: Nicholas Cameron Date: Wed, 21 Aug 2013 13:27:05 +1200 Subject: [PATCH 28/54] Bug 902929. Assert that we don't have a compositable child for ImageBridgeClients. r=nical --- gfx/layers/client/CompositableClient.h | 2 +- gfx/layers/client/ImageClient.h | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/gfx/layers/client/CompositableClient.h b/gfx/layers/client/CompositableClient.h index 580abe0631b9..f82ee029484a 100644 --- a/gfx/layers/client/CompositableClient.h +++ b/gfx/layers/client/CompositableClient.h @@ -102,7 +102,7 @@ public: CompositableChild* GetIPDLActor() const; // should only be called by a CompositableForwarder - void SetIPDLActor(CompositableChild* aChild); + virtual void SetIPDLActor(CompositableChild* aChild); CompositableForwarder* GetForwarder() const { diff --git a/gfx/layers/client/ImageClient.h b/gfx/layers/client/ImageClient.h index b9be99e0e158..212bb098c0d5 100644 --- a/gfx/layers/client/ImageClient.h +++ b/gfx/layers/client/ImageClient.h @@ -181,6 +181,11 @@ public: return TextureInfo(mType); } + virtual void SetIPDLActor(CompositableChild* aChild) MOZ_OVERRIDE + { + MOZ_ASSERT(!aChild, "ImageClientBridge should not have IPDL actor"); + } + protected: uint64_t mAsyncContainerID; ShadowableLayer* mLayer; From ac95649e4868ecd1a19806803819bb948c206ce1 Mon Sep 17 00:00:00 2001 From: Nicholas Cameron Date: Wed, 21 Aug 2013 13:28:53 +1200 Subject: [PATCH 29/54] Bug 902929. Be explicit about re-attaching async video compositable hosts. r=nical --- content/media/plugins/MediaPluginReader.cpp | 5 +++ gfx/layers/composite/CanvasLayerComposite.cpp | 2 +- gfx/layers/composite/CompositableHost.cpp | 1 + gfx/layers/composite/CompositableHost.h | 33 +++++++++++++++---- gfx/layers/composite/ImageLayerComposite.cpp | 2 +- gfx/layers/composite/ThebesLayerComposite.cpp | 2 +- gfx/layers/composite/TiledContentHost.cpp | 6 ++-- gfx/layers/composite/TiledContentHost.h | 4 ++- gfx/layers/ipc/LayerTransactionParent.cpp | 15 ++++++--- gfx/layers/ipc/LayerTransactionParent.h | 4 ++- 10 files changed, 57 insertions(+), 17 deletions(-) diff --git a/content/media/plugins/MediaPluginReader.cpp b/content/media/plugins/MediaPluginReader.cpp index 72aa2fba42b9..1f4e7b8359c5 100644 --- a/content/media/plugins/MediaPluginReader.cpp +++ b/content/media/plugins/MediaPluginReader.cpp @@ -366,6 +366,11 @@ MediaPluginReader::ImageBufferCallback::operator()(size_t aWidth, size_t aHeight rgbImage = mozilla::layers::CreateSharedRGBImage(mImageContainer, nsIntSize(aWidth, aHeight), gfxASurface::ImageFormatRGB16_565); + if (!rgbImage) { + NS_WARNING("Could not create rgb image"); + return nullptr; + } + mImage = rgbImage; return rgbImage->AsSharedImage()->GetBuffer(); case MPAPI::YCbCr: diff --git a/gfx/layers/composite/CanvasLayerComposite.cpp b/gfx/layers/composite/CanvasLayerComposite.cpp index 2eeb3d24607f..a51bffacd4bc 100644 --- a/gfx/layers/composite/CanvasLayerComposite.cpp +++ b/gfx/layers/composite/CanvasLayerComposite.cpp @@ -119,7 +119,7 @@ void CanvasLayerComposite::CleanupResources() { if (mImageHost) { - mImageHost->Detach(); + mImageHost->Detach(this); } mImageHost = nullptr; } diff --git a/gfx/layers/composite/CompositableHost.cpp b/gfx/layers/composite/CompositableHost.cpp index 1ea1ec45d0da..44147890b3ca 100644 --- a/gfx/layers/composite/CompositableHost.cpp +++ b/gfx/layers/composite/CompositableHost.cpp @@ -28,6 +28,7 @@ CompositableHost::CompositableHost(const TextureInfo& aTextureInfo) , mCompositor(nullptr) , mLayer(nullptr) , mAttached(false) + , mKeepAttached(false) { MOZ_COUNT_CTOR(CompositableHost); } diff --git a/gfx/layers/composite/CompositableHost.h b/gfx/layers/composite/CompositableHost.h index 0c862574bd37..f95dc5807c90 100644 --- a/gfx/layers/composite/CompositableHost.h +++ b/gfx/layers/composite/CompositableHost.h @@ -210,19 +210,39 @@ public: virtual TiledLayerComposer* AsTiledLayerComposer() { return nullptr; } - virtual void Attach(Layer* aLayer, Compositor* aCompositor) + typedef uint32_t AttachFlags; + static const AttachFlags NO_FLAGS = 0; + static const AttachFlags ALLOW_REATTACH = 1; + static const AttachFlags KEEP_ATTACHED = 2; + + virtual void Attach(Layer* aLayer, + Compositor* aCompositor, + AttachFlags aFlags = NO_FLAGS) { MOZ_ASSERT(aCompositor, "Compositor is required"); - MOZ_ASSERT(!IsAttached()); + NS_ASSERTION(aFlags & ALLOW_REATTACH || !mAttached, + "Re-attaching compositables must be explicitly authorised"); SetCompositor(aCompositor); SetLayer(aLayer); mAttached = true; + mKeepAttached = aFlags & KEEP_ATTACHED; } - void Detach() + // Detach this compositable host from its layer. + // If we are used for async video, then it is not safe to blindly detach since + // we might be re-attached to a different layer. aLayer is the layer which the + // caller expects us to be attached to, we will only detach if we are in fact + // attached to that layer. If we are part of a normal layer, then we will be + // detached in any case. if aLayer is null, then we will only detach if we are + // not async. + void Detach(Layer* aLayer = nullptr) { - SetLayer(nullptr); - SetCompositor(nullptr); - mAttached = false; + if (!mKeepAttached || + aLayer == mLayer) { + SetLayer(nullptr); + SetCompositor(nullptr); + mAttached = false; + mKeepAttached = false; + } } bool IsAttached() { return mAttached; } @@ -251,6 +271,7 @@ protected: Layer* mLayer; RefPtr mFirstTexture; bool mAttached; + bool mKeepAttached; }; class CompositableParentManager; diff --git a/gfx/layers/composite/ImageLayerComposite.cpp b/gfx/layers/composite/ImageLayerComposite.cpp index d16b6197d0b1..eed1792060e6 100644 --- a/gfx/layers/composite/ImageLayerComposite.cpp +++ b/gfx/layers/composite/ImageLayerComposite.cpp @@ -158,7 +158,7 @@ void ImageLayerComposite::CleanupResources() { if (mImageHost) { - mImageHost->Detach(); + mImageHost->Detach(this); } mImageHost = nullptr; } diff --git a/gfx/layers/composite/ThebesLayerComposite.cpp b/gfx/layers/composite/ThebesLayerComposite.cpp index fd92f51fa939..d26fe8f5ad57 100644 --- a/gfx/layers/composite/ThebesLayerComposite.cpp +++ b/gfx/layers/composite/ThebesLayerComposite.cpp @@ -169,7 +169,7 @@ void ThebesLayerComposite::CleanupResources() { if (mBuffer) { - mBuffer->Detach(); + mBuffer->Detach(this); } mBuffer = nullptr; } diff --git a/gfx/layers/composite/TiledContentHost.cpp b/gfx/layers/composite/TiledContentHost.cpp index b141d42d4c8e..a252b33f0f4b 100644 --- a/gfx/layers/composite/TiledContentHost.cpp +++ b/gfx/layers/composite/TiledContentHost.cpp @@ -67,9 +67,11 @@ TiledLayerBufferComposite::ValidateTile(TiledTexture aTile, } void -TiledContentHost::Attach(Layer* aLayer, Compositor* aCompositor) +TiledContentHost::Attach(Layer* aLayer, + Compositor* aCompositor, + AttachFlags aFlags /* = NO_FLAGS */) { - CompositableHost::Attach(aLayer, aCompositor); + CompositableHost::Attach(aLayer, aCompositor, aFlags); static_cast(aLayer)->EnsureTiled(); } diff --git a/gfx/layers/composite/TiledContentHost.h b/gfx/layers/composite/TiledContentHost.h index e7ca60bde3a4..3c4a55096a11 100644 --- a/gfx/layers/composite/TiledContentHost.h +++ b/gfx/layers/composite/TiledContentHost.h @@ -237,7 +237,9 @@ public: mLowPrecisionVideoMemoryTiledBuffer.SetCompositor(aCompositor); } - virtual void Attach(Layer* aLayer, Compositor* aCompositor) MOZ_OVERRIDE; + virtual void Attach(Layer* aLayer, + Compositor* aCompositor, + AttachFlags aFlags = NO_FLAGS) MOZ_OVERRIDE; #ifdef MOZ_DUMP_PAINTING virtual void Dump(FILE* aFile=nullptr, diff --git a/gfx/layers/ipc/LayerTransactionParent.cpp b/gfx/layers/ipc/LayerTransactionParent.cpp index edbf8de809ff..b0f433202b1d 100644 --- a/gfx/layers/ipc/LayerTransactionParent.cpp +++ b/gfx/layers/ipc/LayerTransactionParent.cpp @@ -405,7 +405,7 @@ LayerTransactionParent::RecvUpdate(const InfallibleTArray& cset, } case Edit::TOpAttachCompositable: { const OpAttachCompositable& op = edit.get_OpAttachCompositable(); - Attach(cast(op.layerParent()), cast(op.compositableParent())); + Attach(cast(op.layerParent()), cast(op.compositableParent()), false); cast(op.compositableParent())->SetCompositorID( mLayerManager->GetCompositor()->GetCompositorID()); break; @@ -414,7 +414,7 @@ LayerTransactionParent::RecvUpdate(const InfallibleTArray& cset, const OpAttachAsyncCompositable& op = edit.get_OpAttachAsyncCompositable(); CompositableParent* compositableParent = CompositableMap::Get(op.containerID()); MOZ_ASSERT(compositableParent, "CompositableParent not found in the map"); - Attach(cast(op.layerParent()), compositableParent); + Attach(cast(op.layerParent()), compositableParent, true); compositableParent->SetCompositorID(mLayerManager->GetCompositor()->GetCompositorID()); break; } @@ -501,7 +501,9 @@ LayerTransactionParent::RecvGetTransform(PLayerParent* aParent, } void -LayerTransactionParent::Attach(ShadowLayerParent* aLayerParent, CompositableParent* aCompositable) +LayerTransactionParent::Attach(ShadowLayerParent* aLayerParent, + CompositableParent* aCompositable, + bool aIsAsyncVideo) { LayerComposite* layer = aLayerParent->AsLayer()->AsLayerComposite(); MOZ_ASSERT(layer); @@ -512,7 +514,12 @@ LayerTransactionParent::Attach(ShadowLayerParent* aLayerParent, CompositablePare CompositableHost* compositable = aCompositable->GetCompositableHost(); MOZ_ASSERT(compositable); layer->SetCompositableHost(compositable); - compositable->Attach(aLayerParent->AsLayer(), compositor); + compositable->Attach(aLayerParent->AsLayer(), + compositor, + aIsAsyncVideo + ? CompositableHost::ALLOW_REATTACH + | CompositableHost::KEEP_ATTACHED + : CompositableHost::NO_FLAGS); } bool diff --git a/gfx/layers/ipc/LayerTransactionParent.h b/gfx/layers/ipc/LayerTransactionParent.h index ec5859e73a2a..974ce508f391 100644 --- a/gfx/layers/ipc/LayerTransactionParent.h +++ b/gfx/layers/ipc/LayerTransactionParent.h @@ -106,7 +106,9 @@ protected: virtual PCompositableParent* AllocPCompositableParent(const TextureInfo& aInfo) MOZ_OVERRIDE; virtual bool DeallocPCompositableParent(PCompositableParent* actor) MOZ_OVERRIDE; - void Attach(ShadowLayerParent* aLayerParent, CompositableParent* aCompositable); + void Attach(ShadowLayerParent* aLayerParent, + CompositableParent* aCompositable, + bool aIsAsyncVideo); private: nsRefPtr mLayerManager; From 418335dc0820b299f9ef42e71985bfd6e2f7a950 Mon Sep 17 00:00:00 2001 From: Nicholas Cameron Date: Thu, 22 Aug 2013 21:10:35 +1200 Subject: [PATCH 30/54] Bug 902330. Reftest for partially transparent layer. r=mattwoodrow --- layout/reftests/bugs/902330-1-ref.html | 24 +++++++++++++++++++ layout/reftests/bugs/902330-1.html | 33 ++++++++++++++++++++++++++ layout/reftests/bugs/reftest.list | 1 + 3 files changed, 58 insertions(+) create mode 100644 layout/reftests/bugs/902330-1-ref.html create mode 100644 layout/reftests/bugs/902330-1.html diff --git a/layout/reftests/bugs/902330-1-ref.html b/layout/reftests/bugs/902330-1-ref.html new file mode 100644 index 000000000000..fbf6a9b27ad2 --- /dev/null +++ b/layout/reftests/bugs/902330-1-ref.html @@ -0,0 +1,24 @@ + + + + + + + + + diff --git a/layout/reftests/bugs/902330-1.html b/layout/reftests/bugs/902330-1.html new file mode 100644 index 000000000000..338908ba6eb2 --- /dev/null +++ b/layout/reftests/bugs/902330-1.html @@ -0,0 +1,33 @@ + + + + + + + +
+
+ + diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index e1e4d5973317..c79d2d1a01bf 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1772,5 +1772,6 @@ test-pref(layout.css.flexbox.enabled,true) == 849407-1.html 849407-1-ref.html == 883987-1f.html 883987-1-ref.html == 890495-1.html 890495-1-ref.html == 894931-1.html 894931-1-ref.html +fuzzy(1,10000) == 902330-1.html 902330-1-ref.html == 897491-1.html 897491-1-ref.html == 897491-2.html 897491-2-ref.html From 6e351b8e9bf45ec95448fb84aa767e5899041c5f Mon Sep 17 00:00:00 2001 From: Nicholas Cameron Date: Thu, 22 Aug 2013 21:10:35 +1200 Subject: [PATCH 31/54] Bug 907901. Remove NS_lround from rect.h. r=Bas --- gfx/2d/Rect.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/gfx/2d/Rect.h b/gfx/2d/Rect.h index e831615b19ca..67482772e1ba 100644 --- a/gfx/2d/Rect.h +++ b/gfx/2d/Rect.h @@ -10,7 +10,8 @@ #include "BaseMargin.h" #include "Point.h" #include "Tools.h" -#include "nsMathUtils.h" + +#include namespace mozilla { namespace gfx { @@ -108,10 +109,10 @@ typedef RectTyped Rect; template IntRectTyped RoundedToInt(const RectTyped& aRect) { - return IntRectTyped(NS_lround(aRect.x), - NS_lround(aRect.y), - NS_lround(aRect.width), - NS_lround(aRect.height)); + return IntRectTyped(int32_t(floorf(aRect.x + 0.5f)), + int32_t(floorf(aRect.y + 0.5f)), + int32_t(floorf(aRect.width + 0.5f)), + int32_t(floorf(aRect.height + 0.5f))); } template From 8fc3495832ae94b26e0d995f4c084631f1c15b01 Mon Sep 17 00:00:00 2001 From: Jon Coppeard Date: Wed, 21 Aug 2013 09:58:25 +0100 Subject: [PATCH 32/54] Bug 907147 - Add SkipRoot to stop rooting analysis poinsoning hash in getNewType() r=terrence --- js/src/jsinfer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 269fef925e30..3e2bda689d98 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -6034,6 +6034,8 @@ ExclusiveContext::getNewType(Class *clasp, TaggedProto proto_, JSFunction *fun_) return NULL; TypeObjectSet::AddPtr p = newTypeObjects.lookupForAdd(TypeObjectSet::Lookup(clasp, proto_)); + SkipRoot skipHash(this, &p); /* Prevent the hash from being poisoned. */ + if (p) { TypeObject *type = *p; From 9df5adf4ffb41ae2032a6dccaac346bf48df852c Mon Sep 17 00:00:00 2001 From: Gabor Krizsanits Date: Thu, 22 Aug 2013 08:26:37 +0200 Subject: [PATCH 33/54] Bug 886237 - Splitting up XPCComponents. r=bholley --- js/xpconnect/src/Sandbox.cpp | 1573 ++++++++++++++++++++++++++++ js/xpconnect/src/XPCComponents.cpp | 1545 +-------------------------- js/xpconnect/src/moz.build | 1 + js/xpconnect/src/xpcprivate.h | 15 + 4 files changed, 1595 insertions(+), 1539 deletions(-) create mode 100644 js/xpconnect/src/Sandbox.cpp diff --git a/js/xpconnect/src/Sandbox.cpp b/js/xpconnect/src/Sandbox.cpp new file mode 100644 index 000000000000..93b05c797ac7 --- /dev/null +++ b/js/xpconnect/src/Sandbox.cpp @@ -0,0 +1,1573 @@ +/* -*- 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/. */ + +/* + * The Components.Sandbox object. + */ + +#include "AccessCheck.h" +#include "jsdbgapi.h" +#include "jsfriendapi.h" +#include "jsproxy.h" +#include "nsContentUtils.h" +#include "nsCxPusher.h" +#include "nsGlobalWindow.h" +#include "nsIDOMWindow.h" +#include "nsIScriptContext.h" +#include "nsIScriptObjectPrincipal.h" +#include "nsIScriptSecurityManager.h" +#include "nsIURI.h" +#include "nsJSEnvironment.h" +#include "nsJSUtils.h" +#include "nsNetUtil.h" +#include "nsNullPrincipal.h" +#include "nsPrincipal.h" +#include "nsXMLHttpRequest.h" +#include "WrapperFactory.h" +#include "XPCJSWeakReference.h" +#include "xpcprivate.h" +#include "XPCQuickStubs.h" +#include "XPCWrapper.h" +#include "XrayWrapper.h" +#include "mozilla/dom/BindingUtils.h" + +using namespace mozilla; +using namespace js; +using namespace xpc; + +using mozilla::dom::DestroyProtoAndIfaceCache; + +NS_IMPL_ISUPPORTS3(SandboxPrivate, + nsIScriptObjectPrincipal, + nsIGlobalObject, + nsISupportsWeakReference) + +const char kScriptSecurityManagerContractID[] = NS_SCRIPTSECURITYMANAGER_CONTRACTID; + +class nsXPCComponents_utils_Sandbox : public nsIXPCComponents_utils_Sandbox, + public nsIXPCScriptable +{ +public: + // Aren't macros nice? + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIXPCCOMPONENTS_UTILS_SANDBOX + NS_DECL_NSIXPCSCRIPTABLE + +public: + nsXPCComponents_utils_Sandbox(); + virtual ~nsXPCComponents_utils_Sandbox(); + +private: + static nsresult CallOrConstruct(nsIXPConnectWrappedNative *wrapper, + JSContext *cx, HandleObject obj, + const CallArgs &args, bool *_retval); +}; + +already_AddRefed +NewSandboxConstructor() +{ + nsCOMPtr sbConstructor = + new nsXPCComponents_utils_Sandbox(); + return sbConstructor.forget(); +} + +static bool +SandboxDump(JSContext *cx, unsigned argc, jsval *vp) +{ + JSString *str; + if (!argc) + return true; + + str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]); + if (!str) + return false; + + size_t length; + const jschar *chars = JS_GetStringCharsZAndLength(cx, str, &length); + if (!chars) + return false; + + nsDependentString wstr(chars, length); + char *cstr = ToNewUTF8String(wstr); + if (!cstr) + return false; + +#if defined(XP_MACOSX) + // Be nice and convert all \r to \n. + char *c = cstr, *cEnd = cstr + strlen(cstr); + while (c < cEnd) { + if (*c == '\r') + *c = '\n'; + c++; + } +#endif + + fputs(cstr, stdout); + fflush(stdout); + NS_Free(cstr); + JS_SET_RVAL(cx, vp, JSVAL_TRUE); + return true; +} + +static bool +SandboxDebug(JSContext *cx, unsigned argc, jsval *vp) +{ +#ifdef DEBUG + return SandboxDump(cx, argc, vp); +#else + return true; +#endif +} + +static bool +SandboxImport(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + if (args.length() < 1 || args[0].isPrimitive()) { + XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx); + return false; + } + + RootedString funname(cx); + if (args.length() > 1) { + // Use the second parameter as the function name. + funname = JS_ValueToString(cx, args[1]); + if (!funname) + return false; + } else { + // NB: funobj must only be used to get the JSFunction out. + RootedObject funobj(cx, &args[0].toObject()); + if (js::IsProxy(funobj)) { + funobj = XPCWrapper::UnsafeUnwrapSecurityWrapper(funobj); + } + + JSAutoCompartment ac(cx, funobj); + + JSFunction *fun = JS_ValueToFunction(cx, ObjectValue(*funobj)); + if (!fun) { + XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx); + return false; + } + + // Use the actual function name as the name. + funname = JS_GetFunctionId(fun); + if (!funname) { + XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx); + return false; + } + } + + RootedId id(cx); + if (!JS_ValueToId(cx, StringValue(funname), id.address())) + return false; + + // We need to resolve the this object, because this function is used + // unbound and should still work and act on the original sandbox. + RootedObject thisObject(cx, JS_THIS_OBJECT(cx, vp)); + if (!thisObject) { + XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx); + return false; + } + if (!JS_SetPropertyById(cx, thisObject, id, args[0])) + return false; + + args.rval().setUndefined(); + return true; +} + +static bool +CreateXMLHttpRequest(JSContext *cx, unsigned argc, jsval *vp) +{ + nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); + if (!ssm) + return false; + + nsIPrincipal *subjectPrincipal = ssm->GetCxSubjectPrincipal(cx); + if (!subjectPrincipal) + return false; + + RootedObject global(cx, JS::CurrentGlobalOrNull(cx)); + MOZ_ASSERT(global); + + nsIScriptObjectPrincipal *sop = + static_cast(xpc_GetJSPrivate(global)); + nsCOMPtr iglobal = do_QueryInterface(sop); + + nsCOMPtr xhr = new nsXMLHttpRequest(); + nsresult rv = xhr->Init(subjectPrincipal, nullptr, iglobal, nullptr); + if (NS_FAILED(rv)) + return false; + + rv = nsContentUtils::WrapNative(cx, global, xhr, vp); + if (NS_FAILED(rv)) + return false; + + return true; +} + +/* + * Instead of simply wrapping a function into another compartment, + * this helper function creates a native function in the target + * compartment and forwards the call to the original function. + * That call will be different than a regular JS function call in + * that, the |this| is left unbound, and all the non-native JS + * object arguments will be cloned using the structured clone + * algorithm. + * The return value is the new forwarder function, wrapped into + * the caller's compartment. + * The 3rd argument is the name of the property that will + * be set on the target scope, with the forwarder function as + * the value. + * The principal of the caller must subsume that of the target. + * + * Expected type of the arguments and the return value: + * function exportFunction(function funToExport, + * object targetScope, + * string name) + */ +static bool +ExportFunction(JSContext *cx, unsigned argc, jsval *vp) +{ + MOZ_ASSERT(cx); + if (argc < 3) { + JS_ReportError(cx, "Function requires at least 3 arguments"); + return false; + } + + CallArgs args = CallArgsFromVp(argc, vp); + if (!args[0].isObject() || !args[1].isObject() || !args[2].isString()) { + JS_ReportError(cx, "Invalid argument"); + return false; + } + + RootedObject funObj(cx, &args[0].toObject()); + RootedObject targetScope(cx, &args[1].toObject()); + RootedString funName(cx, args[2].toString()); + + // We can only export functions to scopes those are transparent for us, + // so if there is a security wrapper around targetScope we must throw. + targetScope = CheckedUnwrap(targetScope); + if (!targetScope) { + JS_ReportError(cx, "Permission denied to export function into scope"); + return false; + } + + if (JS_GetStringLength(funName) == 0) { + JS_ReportError(cx, "3rd argument should be a non-empty string"); + return false; + } + + { + // We need to operate in the target scope from here on, let's enter + // its compartment. + JSAutoCompartment ac(cx, targetScope); + + // Unwrapping to see if we have a callable. + funObj = UncheckedUnwrap(funObj); + if (!JS_ObjectIsCallable(cx, funObj)) { + JS_ReportError(cx, "First argument must be a function"); + return false; + } + + // The function forwarder will live in the target compartment. Since + // this function will be referenced from its private slot, to avoid a + // GC hazard, we must wrap it to the same compartment. + if (!JS_WrapObject(cx, funObj.address())) + return false; + + RootedId id(cx); + if (!JS_ValueToId(cx, args[2], id.address())) + return false; + + // And now, let's create the forwarder function in the target compartment + // for the function the be exported. + if (!NewFunctionForwarder(cx, id, funObj, /* doclone = */ true, args.rval())) { + JS_ReportError(cx, "Exporting function failed"); + return false; + } + + // We have the forwarder function in the target compartment, now + // we have to add it to the target scope as a property. + if (!JS_DefinePropertyById(cx, targetScope, id, args.rval(), + JS_PropertyStub, JS_StrictPropertyStub, + JSPROP_ENUMERATE)) + return false; + } + + // Finally we have to re-wrap the exported function back to the caller compartment. + if (!JS_WrapValue(cx, args.rval().address())) + return false; + + return true; +} + +static bool +GetFilenameAndLineNumber(JSContext *cx, nsACString &filename, unsigned &lineno) +{ + JSScript *script; + if (JS_DescribeScriptedCaller(cx, &script, &lineno)) { + if (const char *cfilename = JS_GetScriptFilename(cx, script)) { + filename.Assign(nsDependentCString(cfilename)); + return true; + } + } + return false; +} + +namespace xpc { +bool +IsReflector(JSObject *obj) +{ + return IS_WN_REFLECTOR(obj) || dom::IsDOMObject(obj); +} +} /* namespace xpc */ + +enum ForwarderCloneTags { + SCTAG_BASE = JS_SCTAG_USER_MIN, + SCTAG_REFLECTOR +}; + +static JSObject * +CloneNonReflectorsRead(JSContext *cx, JSStructuredCloneReader *reader, uint32_t tag, + uint32_t data, void *closure) +{ + MOZ_ASSERT(closure, "Null pointer!"); + AutoObjectVector *reflectors = static_cast(closure); + if (tag == SCTAG_REFLECTOR) { + MOZ_ASSERT(!data); + + size_t idx; + if (JS_ReadBytes(reader, &idx, sizeof(size_t))) { + RootedObject reflector(cx, reflectors->handleAt(idx)); + MOZ_ASSERT(reflector, "No object pointer?"); + MOZ_ASSERT(IsReflector(reflector), "Object pointer must be a reflector!"); + + JS_WrapObject(cx, reflector.address()); + JS_ASSERT(WrapperFactory::IsXrayWrapper(reflector) || + IsReflector(reflector)); + + return reflector; + } + } + + JS_ReportError(cx, "CloneNonReflectorsRead error"); + return nullptr; +} + +static bool +CloneNonReflectorsWrite(JSContext *cx, JSStructuredCloneWriter *writer, + Handle obj, void *closure) +{ + MOZ_ASSERT(closure, "Null pointer!"); + + // We need to maintain a list of reflectors to make sure all these objects + // are properly rooter. Only their indices will be serialized. + AutoObjectVector *reflectors = static_cast(closure); + if (IsReflector(obj)) { + if (!reflectors->append(obj)) + return false; + + size_t idx = reflectors->length()-1; + if (JS_WriteUint32Pair(writer, SCTAG_REFLECTOR, 0) && + JS_WriteBytes(writer, &idx, sizeof(size_t))) { + return true; + } + } + + JS_ReportError(cx, "CloneNonReflectorsWrite error"); + return false; +} + +JSStructuredCloneCallbacks gForwarderStructuredCloneCallbacks = { + CloneNonReflectorsRead, + CloneNonReflectorsWrite, + nullptr +}; + +/* + * This is a special structured cloning, that clones only non-reflectors. + * The function assumes the cx is already entered the compartment we want + * to clone to, and that if val is an object is from the compartment we + * clone from. + */ +bool +CloneNonReflectors(JSContext *cx, MutableHandleValue val) +{ + JSAutoStructuredCloneBuffer buffer; + AutoObjectVector rootedReflectors(cx); + { + // For parsing val we have to enter its compartment. + // (unless it's a primitive) + Maybe ac; + if (val.isObject()) { + ac.construct(cx, &val.toObject()); + } + + if (!buffer.write(cx, val, + &gForwarderStructuredCloneCallbacks, + &rootedReflectors)) + { + return false; + } + } + + // Now recreate the clones in the target compartment. + RootedValue rval(cx); + if (!buffer.read(cx, val.address(), + &gForwarderStructuredCloneCallbacks, + &rootedReflectors)) + { + return false; + } + + return true; +} + +/* + * Similar to evalInSandbox except this one is used to eval a script in the + * scope of a window. Also note, that the return value and the possible exceptions + * in the script are structured cloned, unless they are natives (then they are just + * wrapped). + * Principal of the caller must subsume the target's. + * + * Expected type of the arguments: + * value evalInWindow(string script, + * object window) + */ +static bool +EvalInWindow(JSContext *cx, unsigned argc, jsval *vp) +{ + MOZ_ASSERT(cx); + if (argc < 2) { + JS_ReportError(cx, "Function requires two arguments"); + return false; + } + + CallArgs args = CallArgsFromVp(argc, vp); + if (!args[0].isString() || !args[1].isObject()) { + JS_ReportError(cx, "Invalid arguments"); + return false; + } + + RootedString srcString(cx, args[0].toString()); + RootedObject targetScope(cx, &args[1].toObject()); + + // If we cannot unwrap we must not eval in it. + targetScope = CheckedUnwrap(targetScope); + if (!targetScope) { + JS_ReportError(cx, "Permission denied to eval in target scope"); + return false; + } + + // Make sure that we have a window object. + RootedObject inner(cx, CheckedUnwrap(targetScope, /* stopAtOuter = */ false)); + nsCOMPtr global; + nsCOMPtr window; + if (!JS_IsGlobalObject(inner) || + !(global = GetNativeForGlobal(inner)) || + !(window = do_QueryInterface(global))) + { + JS_ReportError(cx, "Second argument must be a window"); + return false; + } + + nsCOMPtr context = + (static_cast(window.get()))->GetScriptContext(); + if (!context) { + JS_ReportError(cx, "Script context needed"); + return false; + } + + if (!context->GetScriptsEnabled()) { + JS_ReportError(cx, "Scripts are disabled in this window"); + return false; + } + + nsCString filename; + unsigned lineNo; + if (!GetFilenameAndLineNumber(cx, filename, lineNo)) { + // Default values for non-scripted callers. + filename.Assign("Unknown"); + lineNo = 0; + } + + nsDependentJSString srcDepString; + srcDepString.init(cx, srcString); + + { + // CompileOptions must be created from the context + // we will execute this script in. + JSContext *wndCx = context->GetNativeContext(); + AutoCxPusher pusher(wndCx); + JS::CompileOptions compileOptions(wndCx); + compileOptions.setFileAndLine(filename.get(), lineNo); + + // We don't want the JS engine to automatically report + // uncaught exceptions. + nsJSUtils::EvaluateOptions evaluateOptions; + evaluateOptions.setReportUncaught(false); + + nsresult rv = nsJSUtils::EvaluateString(wndCx, + srcDepString, + targetScope, + compileOptions, + evaluateOptions, + args.rval().address()); + + if (NS_FAILED(rv)) { + // If there was an exception we get it as a return value, if + // the evaluation failed for some other reason, then a default + // exception is raised. + MOZ_ASSERT(!JS_IsExceptionPending(wndCx), + "Exception should be delivered as return value."); + if (args.rval().isUndefined()) { + MOZ_ASSERT(rv == NS_ERROR_OUT_OF_MEMORY); + return false; + } + + // If there was an exception thrown we should set it + // on the calling context. + RootedValue exn(wndCx, args.rval()); + // First we should reset the return value. + args.rval().set(UndefinedValue()); + + // Then clone the exception. + if (CloneNonReflectors(cx, &exn)) + JS_SetPendingException(cx, exn); + + return false; + } + } + + // Let's clone the return value back to the callers compartment. + if (!CloneNonReflectors(cx, args.rval())) { + args.rval().set(UndefinedValue()); + return false; + } + + return true; +} + +static bool +sandbox_enumerate(JSContext *cx, HandleObject obj) +{ + return JS_EnumerateStandardClasses(cx, obj); +} + +static bool +sandbox_resolve(JSContext *cx, HandleObject obj, HandleId id) +{ + bool resolved; + return JS_ResolveStandardClass(cx, obj, id, &resolved); +} + +static void +sandbox_finalize(JSFreeOp *fop, JSObject *obj) +{ + nsIScriptObjectPrincipal *sop = + static_cast(xpc_GetJSPrivate(obj)); + MOZ_ASSERT(sop); + static_cast(sop)->ForgetGlobalObject(); + NS_IF_RELEASE(sop); + DestroyProtoAndIfaceCache(obj); +} + +static bool +sandbox_convert(JSContext *cx, HandleObject obj, JSType type, MutableHandleValue vp) +{ + if (type == JSTYPE_OBJECT) { + vp.set(OBJECT_TO_JSVAL(obj)); + return true; + } + + return JS_ConvertStub(cx, obj, type, vp); +} + +static JSClass SandboxClass = { + "Sandbox", + XPCONNECT_GLOBAL_FLAGS, + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, + sandbox_enumerate, sandbox_resolve, sandbox_convert, sandbox_finalize, + NULL, NULL, NULL, NULL, TraceXPCGlobal +}; + +static const JSFunctionSpec SandboxFunctions[] = { + JS_FS("dump", SandboxDump, 1,0), + JS_FS("debug", SandboxDebug, 1,0), + JS_FS("importFunction", SandboxImport, 1,0), + JS_FS_END +}; + +bool +IsSandbox(JSObject *obj) +{ + return GetObjectJSClass(obj) == &SandboxClass; +} + +/***************************************************************************/ +nsXPCComponents_utils_Sandbox::nsXPCComponents_utils_Sandbox() +{ +} + +nsXPCComponents_utils_Sandbox::~nsXPCComponents_utils_Sandbox() +{ +} + +NS_INTERFACE_MAP_BEGIN(nsXPCComponents_utils_Sandbox) + NS_INTERFACE_MAP_ENTRY(nsIXPCComponents_utils_Sandbox) + NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_utils_Sandbox) +NS_INTERFACE_MAP_END_THREADSAFE + +NS_IMPL_ADDREF(nsXPCComponents_utils_Sandbox) +NS_IMPL_RELEASE(nsXPCComponents_utils_Sandbox) + +// We use the nsIXPScriptable macros to generate lots of stuff for us. +#define XPC_MAP_CLASSNAME nsXPCComponents_utils_Sandbox +#define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_utils_Sandbox" +#define XPC_MAP_WANT_CALL +#define XPC_MAP_WANT_CONSTRUCT +#define XPC_MAP_FLAGS 0 +#include "xpc_map_end.h" /* This #undef's the above. */ + +xpc::SandboxProxyHandler xpc::sandboxProxyHandler; + +bool +xpc::IsSandboxPrototypeProxy(JSObject *obj) +{ + return js::IsProxy(obj) && + js::GetProxyHandler(obj) == &xpc::sandboxProxyHandler; +} + +bool +xpc::SandboxCallableProxyHandler::call(JSContext *cx, JS::Handle proxy, + const JS::CallArgs &args) +{ + // We forward the call to our underlying callable. + + // The parent of our proxy is the SandboxProxyHandler proxy + RootedObject sandboxProxy(cx, JS_GetParent(proxy)); + MOZ_ASSERT(js::IsProxy(sandboxProxy) && + js::GetProxyHandler(sandboxProxy) == &xpc::sandboxProxyHandler); + + // The parent of the sandboxProxy is the sandbox global, and the + // target object is the original proto. + RootedObject sandboxGlobal(cx, JS_GetParent(sandboxProxy)); + MOZ_ASSERT(js::GetObjectJSClass(sandboxGlobal) == &SandboxClass); + + // If our this object is the sandbox global, we call with this set to the + // original proto instead. + // + // There are two different ways we can compute |this|. If we use + // JS_THIS_VALUE, we'll get the bonafide |this| value as passed by the + // caller, which may be undefined if a global function was invoked without + // an explicit invocant. If we use JS_THIS or JS_THIS_OBJECT, the |this| + // in |vp| will be coerced to the global, which is not the correct + // behavior in ES5 strict mode. And we have no way to compute strictness + // here. + // + // The naive approach is simply to use JS_THIS_VALUE here. If |this| was + // explicit, we can remap it appropriately. If it was implicit, then we + // leave it as undefined, and let the callee sort it out. Since the callee + // is generally in the same compartment as its global (eg the Window's + // compartment, not the Sandbox's), the callee will generally compute the + // correct |this|. + // + // However, this breaks down in the Xray case. If the sandboxPrototype + // is an Xray wrapper, then we'll end up reifying the native methods in + // the Sandbox's scope, which means that they'll compute |this| to be the + // Sandbox, breaking old-style XPC_WN_CallMethod methods. + // + // Luckily, the intent of Xrays is to provide a vanilla view of a foreign + // DOM interface, which means that we don't care about script-enacted + // strictness in the prototype's home compartment. Indeed, since DOM + // methods are always non-strict, we can just assume non-strict semantics + // if the sandboxPrototype is an Xray Wrapper, which lets us appropriately + // remap |this|. + JS::Value thisVal = + WrapperFactory::IsXrayWrapper(sandboxProxy) ? args.computeThis(cx) : args.thisv(); + if (thisVal == ObjectValue(*sandboxGlobal)) { + thisVal = ObjectValue(*js::GetProxyTargetObject(sandboxProxy)); + } + + return JS::Call(cx, thisVal, js::GetProxyPrivate(proxy), args.length(), args.array(), + args.rval()); +} + +xpc::SandboxCallableProxyHandler xpc::sandboxCallableProxyHandler; + +// Wrap a callable such that if we're called with oldThisObj as the +// "this" we will instead call it with newThisObj as the this. +static JSObject* +WrapCallable(JSContext *cx, JSObject *callable, JSObject *sandboxProtoProxy) +{ + MOZ_ASSERT(JS_ObjectIsCallable(cx, callable)); + // Our proxy is wrapping the callable. So we need to use the + // callable as the private. We use the given sandboxProtoProxy as + // the parent, and our call() hook depends on that. + MOZ_ASSERT(js::IsProxy(sandboxProtoProxy) && + js::GetProxyHandler(sandboxProtoProxy) == + &xpc::sandboxProxyHandler); + + RootedValue priv(cx, ObjectValue(*callable)); + return js::NewProxyObject(cx, &xpc::sandboxCallableProxyHandler, + priv, nullptr, + sandboxProtoProxy, js::ProxyIsCallable); +} + +template +bool BindPropertyOp(JSContext *cx, Op &op, JSPropertyDescriptor *desc, HandleId id, + unsigned attrFlag, HandleObject sandboxProtoProxy) +{ + if (!op) { + return true; + } + + RootedObject func(cx); + if (desc->attrs & attrFlag) { + // Already an object + func = JS_FUNC_TO_DATA_PTR(JSObject *, op); + } else { + // We have an actual property op. For getters, we use 0 + // args, for setters we use 1 arg. + uint32_t args = (attrFlag == JSPROP_GETTER) ? 0 : 1; + RootedObject obj(cx, desc->obj); + func = GeneratePropertyOp(cx, obj, id, args, op); + if (!func) + return false; + } + func = WrapCallable(cx, func, sandboxProtoProxy); + if (!func) + return false; + op = JS_DATA_TO_FUNC_PTR(Op, func.get()); + desc->attrs |= attrFlag; + return true; +} + +extern bool +XPC_WN_Helper_GetProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp); +extern bool +XPC_WN_Helper_SetProperty(JSContext *cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp); + +bool +xpc::SandboxProxyHandler::getPropertyDescriptor(JSContext *cx, + JS::Handle proxy, + JS::Handle id, + JS::MutableHandle desc, + unsigned flags) +{ + JS::RootedObject obj(cx, wrappedObject(proxy)); + + MOZ_ASSERT(js::GetObjectCompartment(obj) == js::GetObjectCompartment(proxy)); + if (!JS_GetPropertyDescriptorById(cx, obj, id, + flags, desc)) + return false; + + if (!desc.object()) + return true; // No property, nothing to do + + // Now fix up the getter/setter/value as needed to be bound to desc->obj + // Don't mess with holder_get and holder_set, though, because those rely on + // the "vp is prefilled with the value in the slot" behavior that property + // ops can in theory rely on, but our property op forwarder doesn't know how + // to make that happen. Since we really only need to rebind the DOM methods + // here, not rebindings holder_get and holder_set is OK. + // + // Similarly, don't mess with XPC_WN_Helper_GetProperty and + // XPC_WN_Helper_SetProperty, for the same reasons: that could confuse our + // access to expandos when we're not doing Xrays. + if (desc.getter() != xpc::holder_get && + desc.getter() != XPC_WN_Helper_GetProperty && + !BindPropertyOp(cx, desc.getter(), desc.address(), id, JSPROP_GETTER, proxy)) + return false; + if (desc.setter() != xpc::holder_set && + desc.setter() != XPC_WN_Helper_SetProperty && + !BindPropertyOp(cx, desc.setter(), desc.address(), id, JSPROP_SETTER, proxy)) + return false; + if (desc.value().isObject()) { + JSObject* val = &desc.value().toObject(); + if (JS_ObjectIsCallable(cx, val)) { + val = WrapCallable(cx, val, proxy); + if (!val) + return false; + desc.value().setObject(*val); + } + } + + return true; +} + +bool +xpc::SandboxProxyHandler::getOwnPropertyDescriptor(JSContext *cx, + JS::Handle proxy, + JS::Handle id, + JS::MutableHandle desc, + unsigned flags) +{ + if (!getPropertyDescriptor(cx, proxy, id, desc, flags)) + return false; + + if (desc.object() != wrappedObject(proxy)) + desc.object().set(nullptr); + + return true; +} + +/* + * Reuse the BaseProxyHandler versions of the derived traps that are implemented + * in terms of the fundamental traps. + */ + +bool +xpc::SandboxProxyHandler::has(JSContext *cx, JS::Handle proxy, + JS::Handle id, bool *bp) +{ + return BaseProxyHandler::has(cx, proxy, id, bp); +} +bool +xpc::SandboxProxyHandler::hasOwn(JSContext *cx, JS::Handle proxy, + JS::Handle id, bool *bp) +{ + return BaseProxyHandler::hasOwn(cx, proxy, id, bp); +} + +bool +xpc::SandboxProxyHandler::get(JSContext *cx, JS::Handle proxy, + JS::Handle receiver, + JS::Handle id, + JS::MutableHandle vp) +{ + return BaseProxyHandler::get(cx, proxy, receiver, id, vp); +} + +bool +xpc::SandboxProxyHandler::set(JSContext *cx, JS::Handle proxy, + JS::Handle receiver, + JS::Handle id, + bool strict, + JS::MutableHandle vp) +{ + return BaseProxyHandler::set(cx, proxy, receiver, id, strict, vp); +} + +bool +xpc::SandboxProxyHandler::keys(JSContext *cx, JS::Handle proxy, + AutoIdVector &props) +{ + return BaseProxyHandler::keys(cx, proxy, props); +} + +bool +xpc::SandboxProxyHandler::iterate(JSContext *cx, JS::Handle proxy, + unsigned flags, JS::MutableHandle vp) +{ + return BaseProxyHandler::iterate(cx, proxy, flags, vp); +} + +nsresult +xpc_CreateSandboxObject(JSContext *cx, jsval *vp, nsISupports *prinOrSop, SandboxOptions& options) +{ + // Create the sandbox global object + nsresult rv; + nsCOMPtr xpc(do_GetService(nsIXPConnect::GetCID(), &rv)); + if (NS_FAILED(rv)) + return NS_ERROR_XPC_UNEXPECTED; + + nsCOMPtr principal = do_QueryInterface(prinOrSop); + if (!principal) { + nsCOMPtr sop = do_QueryInterface(prinOrSop); + if (sop) { + principal = sop->GetPrincipal(); + } else { + principal = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv); + MOZ_ASSERT(NS_FAILED(rv) || principal, "Bad return from do_CreateInstance"); + + if (!principal || NS_FAILED(rv)) { + if (NS_SUCCEEDED(rv)) { + rv = NS_ERROR_FAILURE; + } + + return rv; + } + } + MOZ_ASSERT(principal); + } + + JS::CompartmentOptions compartmentOptions; + compartmentOptions.setZone(options.sameZoneAs + ? JS::SameZoneAs(js::UncheckedUnwrap(options.sameZoneAs)) + : JS::SystemZone); + RootedObject sandbox(cx, xpc::CreateGlobalObject(cx, &SandboxClass, + principal, compartmentOptions)); + if (!sandbox) + return NS_ERROR_FAILURE; + + // Set up the wantXrays flag, which indicates whether xrays are desired even + // for same-origin access. + // + // This flag has historically been ignored for chrome sandboxes due to + // quirks in the wrapping implementation that have now been removed. Indeed, + // same-origin Xrays for chrome->chrome access seems a bit superfluous. + // Arguably we should just flip the default for chrome and still honor the + // flag, but such a change would break code in subtle ways for minimal + // benefit. So we just switch it off here. + xpc::GetCompartmentPrivate(sandbox)->wantXrays = + AccessCheck::isChrome(sandbox) ? false : options.wantXrays; + + { + JSAutoCompartment ac(cx, sandbox); + + if (options.proto) { + bool ok = JS_WrapObject(cx, options.proto.address()); + if (!ok) + return NS_ERROR_XPC_UNEXPECTED; + + if (xpc::WrapperFactory::IsXrayWrapper(options.proto) && !options.wantXrays) { + RootedValue v(cx, ObjectValue(*options.proto)); + if (!xpc::WrapperFactory::WaiveXrayAndWrap(cx, v.address())) + return NS_ERROR_FAILURE; + options.proto = &v.toObject(); + } + + // Now check what sort of thing we've got in |proto| + JSObject *unwrappedProto = js::UncheckedUnwrap(options.proto, false); + js::Class *unwrappedClass = js::GetObjectClass(unwrappedProto); + if (IS_WN_CLASS(unwrappedClass) || + mozilla::dom::IsDOMClass(Jsvalify(unwrappedClass))) { + // Wrap it up in a proxy that will do the right thing in terms + // of this-binding for methods. + RootedValue priv(cx, ObjectValue(*options.proto)); + options.proto = js::NewProxyObject(cx, &xpc::sandboxProxyHandler, + priv, nullptr, sandbox); + if (!options.proto) + return NS_ERROR_OUT_OF_MEMORY; + } + + ok = JS_SetPrototype(cx, sandbox, options.proto); + if (!ok) + return NS_ERROR_XPC_UNEXPECTED; + } + + nsCOMPtr sbp = + new SandboxPrivate(principal, sandbox); + + // Pass on ownership of sbp to |sandbox|. + JS_SetPrivate(sandbox, sbp.forget().get()); + + bool allowComponents = nsContentUtils::IsSystemPrincipal(principal) || + nsContentUtils::IsExpandedPrincipal(principal); + if (options.wantComponents && allowComponents && + !nsXPCComponents::AttachComponentsObject(cx, GetObjectScope(sandbox))) + return NS_ERROR_XPC_UNEXPECTED; + + if (!XPCNativeWrapper::AttachNewConstructorObject(cx, sandbox)) + return NS_ERROR_XPC_UNEXPECTED; + + if (!JS_DefineFunctions(cx, sandbox, SandboxFunctions)) + return NS_ERROR_XPC_UNEXPECTED; + + if (options.wantXHRConstructor && + !JS_DefineFunction(cx, sandbox, "XMLHttpRequest", CreateXMLHttpRequest, 0, JSFUN_CONSTRUCTOR)) + return NS_ERROR_XPC_UNEXPECTED; + + if (options.wantExportHelpers && + (!JS_DefineFunction(cx, sandbox, "exportFunction", ExportFunction, 3, 0) || + !JS_DefineFunction(cx, sandbox, "evalInWindow", EvalInWindow, 2, 0))) + return NS_ERROR_XPC_UNEXPECTED; + + } + + if (vp) { + // We have this crazy behavior where wantXrays=false also implies that the + // returned sandbox is implicitly waived. We've stopped advertising it, but + // keep supporting it for now. + *vp = OBJECT_TO_JSVAL(sandbox); + if (options.wantXrays && !JS_WrapValue(cx, vp)) + return NS_ERROR_UNEXPECTED; + if (!options.wantXrays && !xpc::WrapperFactory::WaiveXrayAndWrap(cx, vp)) + return NS_ERROR_UNEXPECTED; + } + + // Set the location information for the new global, so that tools like + // about:memory may use that information + xpc::SetLocationForGlobal(sandbox, options.sandboxName); + + JS_FireOnNewGlobalObject(cx, sandbox); + + return NS_OK; +} + +/* bool call(in nsIXPConnectWrappedNative wrapper, + in JSContextPtr cx, + in JSObjectPtr obj, + in uint32_t argc, + in JSValPtr argv, + in JSValPtr vp); +*/ +NS_IMETHODIMP +nsXPCComponents_utils_Sandbox::Call(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *objArg, const CallArgs &args, bool *_retval) +{ + RootedObject obj(cx, objArg); + return CallOrConstruct(wrapper, cx, obj, args, _retval); +} + +/* bool construct(in nsIXPConnectWrappedNative wrapper, + in JSContextPtr cx, + in JSObjectPtr obj, + in uint32_t argc, + in JSValPtr argv, + in JSValPtr vp); +*/ +NS_IMETHODIMP +nsXPCComponents_utils_Sandbox::Construct(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *objArg, const CallArgs &args, bool *_retval) +{ + RootedObject obj(cx, objArg); + return CallOrConstruct(wrapper, cx, obj, args, _retval); +} + +// for sandbox constructor the first argument can be a URI string in which case +// we use the related Codebase Principal for the sandbox +nsresult +GetPrincipalFromString(JSContext *cx, HandleString codebase, nsIPrincipal **principal) +{ + MOZ_ASSERT(principal); + MOZ_ASSERT(codebase); + nsCOMPtr uri; + nsDependentJSString codebaseStr; + NS_ENSURE_TRUE(codebaseStr.init(cx, codebase), NS_ERROR_FAILURE); + nsresult rv = NS_NewURI(getter_AddRefs(uri), codebaseStr); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr secman = + do_GetService(kScriptSecurityManagerContractID); + NS_ENSURE_TRUE(secman, NS_ERROR_FAILURE); + + // We could allow passing in the app-id and browser-element info to the + // sandbox constructor. But creating a sandbox based on a string is a + // deprecated API so no need to add features to it. + rv = secman->GetNoAppCodebasePrincipal(uri, principal); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(*principal, NS_ERROR_FAILURE); + + return NS_OK; +} + +// for sandbox constructor the first argument can be a principal object or +// a script object principal (Document, Window) +nsresult +GetPrincipalOrSOP(JSContext *cx, HandleObject from, nsISupports **out) +{ + MOZ_ASSERT(out); + *out = NULL; + + nsXPConnect* xpc = nsXPConnect::XPConnect(); + nsCOMPtr wrapper; + xpc->GetWrappedNativeOfJSObject(cx, from, + getter_AddRefs(wrapper)); + + NS_ENSURE_TRUE(wrapper, NS_ERROR_INVALID_ARG); + + if (nsCOMPtr sop = do_QueryWrappedNative(wrapper)) { + sop.forget(out); + return NS_OK; + } + + nsCOMPtr principal = do_QueryWrappedNative(wrapper); + principal.forget(out); + NS_ENSURE_TRUE(*out, NS_ERROR_INVALID_ARG); + + return NS_OK; +} + +// the first parameter of the sandbox constructor might be an array of principals, either in string +// format or actual objects (see GetPrincipalOrSOP) +nsresult +GetExpandedPrincipal(JSContext *cx, HandleObject arrayObj, nsIExpandedPrincipal **out) +{ + MOZ_ASSERT(out); + uint32_t length; + + if (!JS_IsArrayObject(cx, arrayObj) || + !JS_GetArrayLength(cx, arrayObj, &length) || + !length) + { + // we need a white list of principals or uri strings to create an + // expanded principal, if we got an empty array or something else + // report error + return NS_ERROR_INVALID_ARG; + } + + nsTArray< nsCOMPtr > allowedDomains(length); + allowedDomains.SetLength(length); + nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); + NS_ENSURE_TRUE(ssm, NS_ERROR_XPC_UNEXPECTED); + + for (uint32_t i = 0; i < length; ++i) { + RootedValue allowed(cx); + if (!JS_GetElement(cx, arrayObj, i, &allowed)) + return NS_ERROR_INVALID_ARG; + + nsresult rv; + nsCOMPtr principal; + if (allowed.isString()) { + // in case of string let's try to fetch a codebase principal from it + RootedString str(cx, allowed.toString()); + rv = GetPrincipalFromString(cx, str, getter_AddRefs(principal)); + NS_ENSURE_SUCCESS(rv, rv); + } else if (allowed.isObject()) { + // in case of object let's see if it's a Principal or a ScriptObjectPrincipal + nsCOMPtr prinOrSop; + RootedObject obj(cx, &allowed.toObject()); + rv = GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr sop(do_QueryInterface(prinOrSop)); + principal = do_QueryInterface(prinOrSop); + if (sop) { + principal = sop->GetPrincipal(); + } + } + NS_ENSURE_TRUE(principal, NS_ERROR_INVALID_ARG); + + // We do not allow ExpandedPrincipals to contain any system principals + bool isSystem; + rv = ssm->IsSystemPrincipal(principal, &isSystem); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_FALSE(isSystem, NS_ERROR_INVALID_ARG); + allowedDomains[i] = principal; + } + + nsCOMPtr result = new nsExpandedPrincipal(allowedDomains); + result.forget(out); + return NS_OK; +} + +// helper that tries to get a property form the options object +nsresult +GetPropFromOptions(JSContext *cx, HandleObject from, const char *name, MutableHandleValue prop, + bool *found) +{ + if (!JS_HasProperty(cx, from, name, found)) + return NS_ERROR_INVALID_ARG; + + if (found && !JS_GetProperty(cx, from, name, prop)) + return NS_ERROR_INVALID_ARG; + + return NS_OK; +} + +// helper that tries to get a boolean property form the options object +nsresult +GetBoolPropFromOptions(JSContext *cx, HandleObject from, const char *name, bool *prop) +{ + MOZ_ASSERT(prop); + + + RootedValue value(cx); + bool found; + if (NS_FAILED(GetPropFromOptions(cx, from, name, &value, &found))) + return NS_ERROR_INVALID_ARG; + + if (!found) + return NS_OK; + + if (!value.isBoolean()) + return NS_ERROR_INVALID_ARG; + + *prop = value.toBoolean(); + return NS_OK; +} + +// helper that tries to get an object property form the options object +nsresult +GetObjPropFromOptions(JSContext *cx, HandleObject from, const char *name, JSObject **prop) +{ + MOZ_ASSERT(prop); + + RootedValue value(cx); + bool found; + if (NS_FAILED(GetPropFromOptions(cx, from, name, &value, &found))) + return NS_ERROR_INVALID_ARG; + + if (!found) { + *prop = NULL; + return NS_OK; + } + + if (!value.isObject()) + return NS_ERROR_INVALID_ARG; + + *prop = &value.toObject(); + return NS_OK; +} + +// helper that tries to get a string property form the options object +nsresult +GetStringPropFromOptions(JSContext *cx, HandleObject from, const char *name, nsCString &prop) +{ + RootedValue value(cx); + bool found; + nsresult rv = GetPropFromOptions(cx, from, name, &value, &found); + NS_ENSURE_SUCCESS(rv, rv); + + if (!found) + return NS_OK; + + NS_ENSURE_TRUE(value.isString(), NS_ERROR_INVALID_ARG); + + char *tmp = JS_EncodeString(cx, value.toString()); + NS_ENSURE_TRUE(tmp, NS_ERROR_INVALID_ARG); + prop.Adopt(tmp, strlen(tmp)); + return NS_OK; +} + +// helper that parsing the sandbox options object (from) and sets the fields of the incoming options struct (options) +nsresult +ParseOptionsObject(JSContext *cx, jsval from, SandboxOptions &options) +{ + NS_ENSURE_TRUE(from.isObject(), NS_ERROR_INVALID_ARG); + RootedObject optionsObject(cx, &from.toObject()); + nsresult rv = GetObjPropFromOptions(cx, optionsObject, + "sandboxPrototype", options.proto.address()); + NS_ENSURE_SUCCESS(rv, rv); + + rv = GetBoolPropFromOptions(cx, optionsObject, + "wantXrays", &options.wantXrays); + NS_ENSURE_SUCCESS(rv, rv); + + rv = GetBoolPropFromOptions(cx, optionsObject, + "wantComponents", &options.wantComponents); + NS_ENSURE_SUCCESS(rv, rv); + + rv = GetBoolPropFromOptions(cx, optionsObject, + "wantXHRConstructor", &options.wantXHRConstructor); + NS_ENSURE_SUCCESS(rv, rv); + + rv = GetBoolPropFromOptions(cx, optionsObject, + "wantExportHelpers", &options.wantExportHelpers); + NS_ENSURE_SUCCESS(rv, rv); + + rv = GetStringPropFromOptions(cx, optionsObject, + "sandboxName", options.sandboxName); + NS_ENSURE_SUCCESS(rv, rv); + + rv = GetObjPropFromOptions(cx, optionsObject, + "sameZoneAs", options.sameZoneAs.address()); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +static nsresult +AssembleSandboxMemoryReporterName(JSContext *cx, nsCString &sandboxName) +{ + // Use a default name when the caller did not provide a sandboxName. + if (sandboxName.IsEmpty()) + sandboxName = NS_LITERAL_CSTRING("[anonymous sandbox]"); + + nsXPConnect* xpc = nsXPConnect::XPConnect(); + // Get the xpconnect native call context. + nsAXPCNativeCallContext *cc = nullptr; + xpc->GetCurrentNativeCallContext(&cc); + NS_ENSURE_TRUE(cc, NS_ERROR_INVALID_ARG); + + // Get the current source info from xpc. + nsCOMPtr frame; + xpc->GetCurrentJSStack(getter_AddRefs(frame)); + + // Append the caller's location information. + if (frame) { + nsCString location; + int32_t lineNumber = 0; + frame->GetFilename(getter_Copies(location)); + frame->GetLineNumber(&lineNumber); + + sandboxName.AppendLiteral(" (from: "); + sandboxName.Append(location); + sandboxName.AppendLiteral(":"); + sandboxName.AppendInt(lineNumber); + sandboxName.AppendLiteral(")"); + } + + return NS_OK; +} + +// static +nsresult +nsXPCComponents_utils_Sandbox::CallOrConstruct(nsIXPConnectWrappedNative *wrapper, + JSContext *cx, HandleObject obj, + const CallArgs &args, bool *_retval) +{ + if (args.length() < 1) + return ThrowAndFail(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx, _retval); + + nsresult rv; + + // Make sure to set up principals on the sandbox before initing classes + nsCOMPtr principal; + nsCOMPtr expanded; + nsCOMPtr prinOrSop; + + if (args[0].isString()) { + RootedString str(cx, args[0].toString()); + rv = GetPrincipalFromString(cx, str, getter_AddRefs(principal)); + prinOrSop = principal; + } else if (args[0].isObject()) { + RootedObject obj(cx, &args[0].toObject()); + if (JS_IsArrayObject(cx, obj)) { + rv = GetExpandedPrincipal(cx, obj, getter_AddRefs(expanded)); + prinOrSop = expanded; + } else { + rv = GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop)); + } + } else { + return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); + } + + if (NS_FAILED(rv)) + return ThrowAndFail(rv, cx, _retval); + + SandboxOptions options(cx); + + if (args.length() > 1 && args[1].isObject()) { + if (NS_FAILED(ParseOptionsObject(cx, args[1], options))) + return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); + } + + if (NS_FAILED(AssembleSandboxMemoryReporterName(cx, options.sandboxName))) + return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); + + rv = xpc_CreateSandboxObject(cx, args.rval().address(), prinOrSop, options); + + if (NS_FAILED(rv)) + return ThrowAndFail(rv, cx, _retval); + + *_retval = true; + + return rv; +} + +class ContextHolder : public nsIScriptObjectPrincipal +{ +public: + ContextHolder(JSContext *aOuterCx, HandleObject aSandbox, nsIPrincipal *aPrincipal); + virtual ~ContextHolder(); + + JSContext * GetJSContext() + { + return mJSContext; + } + + nsIPrincipal * GetPrincipal() { return mPrincipal; } + + NS_DECL_ISUPPORTS + +private: + JSContext* mJSContext; + nsCOMPtr mPrincipal; +}; + +NS_IMPL_ISUPPORTS1(ContextHolder, nsIScriptObjectPrincipal) + +ContextHolder::ContextHolder(JSContext *aOuterCx, + HandleObject aSandbox, + nsIPrincipal *aPrincipal) + : mJSContext(JS_NewContext(JS_GetRuntime(aOuterCx), 1024)), + mPrincipal(aPrincipal) +{ + if (mJSContext) { + bool isChrome; + DebugOnly rv = XPCWrapper::GetSecurityManager()-> + IsSystemPrincipal(mPrincipal, &isChrome); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + JS_SetOptions(mJSContext, + JS_GetOptions(mJSContext) | + JSOPTION_DONT_REPORT_UNCAUGHT | + JSOPTION_PRIVATE_IS_NSISUPPORTS); + js::SetDefaultObjectForContext(mJSContext, aSandbox); + JS_SetContextPrivate(mJSContext, this); + } +} + +ContextHolder::~ContextHolder() +{ + if (mJSContext) + JS_DestroyContextNoGC(mJSContext); +} + +nsresult +xpc_EvalInSandbox(JSContext *cx, HandleObject sandboxArg, const nsAString& source, + const char *filename, int32_t lineNo, + JSVersion jsVersion, bool returnStringOnly, MutableHandleValue rval) +{ + JS_AbortIfWrongThread(JS_GetRuntime(cx)); + rval.set(UndefinedValue()); + + bool waiveXray = xpc::WrapperFactory::HasWaiveXrayFlag(sandboxArg); + RootedObject sandbox(cx, js::CheckedUnwrap(sandboxArg)); + if (!sandbox || js::GetObjectJSClass(sandbox) != &SandboxClass) { + return NS_ERROR_INVALID_ARG; + } + + nsIScriptObjectPrincipal *sop = + (nsIScriptObjectPrincipal*)xpc_GetJSPrivate(sandbox); + MOZ_ASSERT(sop, "Invalid sandbox passed"); + nsCOMPtr prin = sop->GetPrincipal(); + NS_ENSURE_TRUE(prin, NS_ERROR_FAILURE); + + nsAutoCString filenameBuf; + if (!filename) { + // Default to the spec of the principal. + nsJSPrincipals::get(prin)->GetScriptLocation(filenameBuf); + filename = filenameBuf.get(); + lineNo = 1; + } + + // We create a separate cx to do the sandbox evaluation. Scope it. + RootedValue v(cx, UndefinedValue()); + RootedValue exn(cx, UndefinedValue()); + bool ok = true; + { + // Make a special cx for the sandbox and push it. + // NB: As soon as the RefPtr goes away, the cx goes away. So declare + // it first so that it disappears last. + nsRefPtr sandcxHolder = new ContextHolder(cx, sandbox, prin); + JSContext *sandcx = sandcxHolder->GetJSContext(); + if (!sandcx) { + JS_ReportError(cx, "Can't prepare context for evalInSandbox"); + return NS_ERROR_OUT_OF_MEMORY; + } + nsCxPusher pusher; + pusher.Push(sandcx); + JSAutoCompartment ac(sandcx, sandbox); + + JS::CompileOptions options(sandcx); + options.setPrincipals(nsJSPrincipals::get(prin)) + .setFileAndLine(filename, lineNo); + if (jsVersion != JSVERSION_DEFAULT) + options.setVersion(jsVersion); + JS::RootedObject rootedSandbox(sandcx, sandbox); + ok = JS::Evaluate(sandcx, rootedSandbox, options, + PromiseFlatString(source).get(), source.Length(), + v.address()); + if (ok && returnStringOnly && !v.isUndefined()) { + JSString *str = JS_ValueToString(sandcx, v); + ok = !!str; + v = ok ? JS::StringValue(str) : JS::UndefinedValue(); + } + + // If the sandbox threw an exception, grab it off the context. + if (JS_GetPendingException(sandcx, exn.address())) { + MOZ_ASSERT(!ok); + JS_ClearPendingException(sandcx); + if (returnStringOnly) { + // The caller asked for strings only, convert the + // exception into a string. + JSString *str = JS_ValueToString(sandcx, exn); + exn = str ? JS::StringValue(str) : JS::UndefinedValue(); + } + } + } + + // + // Alright, we're back on the caller's cx. If an error occured, try to + // wrap and set the exception. Otherwise, wrap the return value. + // + + if (!ok) { + // If we end up without an exception, it was probably due to OOM along + // the way, in which case we thow. Otherwise, wrap it. + if (exn.isUndefined() || !JS_WrapValue(cx, exn.address())) + return NS_ERROR_OUT_OF_MEMORY; + + // Set the exception on our caller's cx. + JS_SetPendingException(cx, exn); + return NS_ERROR_FAILURE; + } + + // Transitively apply Xray waivers if |sb| was waived. + if (waiveXray) { + ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, v.address()); + } else { + ok = JS_WrapValue(cx, v.address()); + } + NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE); + + // Whew! + rval.set(v); + return NS_OK; +} + +bool +NonCloningFunctionForwarder(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0)); + MOZ_ASSERT(v.isObject(), "weird function"); + + JSObject *obj = JS_THIS_OBJECT(cx, vp); + if (!obj) { + return false; + } + return JS_CallFunctionValue(cx, obj, v, args.length(), args.array(), vp); +} + +/* + * Forwards the call to the exported function. Clones all the non reflectors, ignores + * the |this| argument. + */ +bool +CloningFunctionForwarder(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0)); + NS_ASSERTION(v.isObject(), "weird function"); + RootedObject origFunObj(cx, UncheckedUnwrap(&v.toObject())); + { + JSAutoCompartment ac(cx, origFunObj); + // Note: only the arguments are cloned not the |this| or the |callee|. + // Function forwarder does not use those. + for (unsigned i = 0; i < args.length(); i++) { + if (!CloneNonReflectors(cx, args[i])) { + return false; + } + } + + // JS API does not support any JSObject to JSFunction conversion, + // so let's use JS_CallFunctionValue instead. + RootedValue functionVal(cx); + functionVal.setObject(*origFunObj); + + if (!JS_CallFunctionValue(cx, nullptr, functionVal, args.length(), args.array(), vp)) + return false; + } + + // Return value must be wrapped. + return JS_WrapValue(cx, vp); +} + +bool +NewFunctionForwarder(JSContext *cx, HandleId id, HandleObject callable, bool doclone, + MutableHandleValue vp) +{ + JSFunction *fun = js::NewFunctionByIdWithReserved(cx, doclone ? CloningFunctionForwarder : + NonCloningFunctionForwarder, + 0,0, JS::CurrentGlobalOrNull(cx), id); + + if (!fun) + return false; + + JSObject *funobj = JS_GetFunctionObject(fun); + js::SetFunctionNativeReserved(funobj, 0, ObjectValue(*callable)); + vp.setObject(*funobj); + return true; +} diff --git a/js/xpconnect/src/XPCComponents.cpp b/js/xpconnect/src/XPCComponents.cpp index 1ae811156239..c385e73a336b 100644 --- a/js/xpconnect/src/XPCComponents.cpp +++ b/js/xpconnect/src/XPCComponents.cpp @@ -40,17 +40,15 @@ #include "nsDOMClassInfoID.h" #include "nsGlobalWindow.h" - using namespace mozilla; using namespace js; using namespace xpc; -using mozilla::dom::DestroyProtoAndIfaceCache; - /***************************************************************************/ // stuff used by all -static nsresult ThrowAndFail(nsresult errNum, JSContext* cx, bool* retval) +nsresult +ThrowAndFail(nsresult errNum, JSContext* cx, bool* retval) { XPCThrower::Throw(errNum, cx); *retval = false; @@ -102,7 +100,6 @@ char * xpc_CheckAccessList(const PRUnichar* wideName, const char* const list[]) /***************************************************************************/ - class nsXPCComponents_Interfaces : public nsIXPCComponents_Interfaces, public nsIXPCScriptable, @@ -2588,27 +2585,6 @@ nsXPCComponents_Constructor::HasInstance(nsIXPConnectWrappedNative *wrapper, return NS_OK; } -/***************************************************************************/ -// Javascript constructor for the sandbox object -class nsXPCComponents_utils_Sandbox : public nsIXPCComponents_utils_Sandbox, - public nsIXPCScriptable -{ -public: - // Aren't macros nice? - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_NSIXPCCOMPONENTS_UTILS_SANDBOX - NS_DECL_NSIXPCSCRIPTABLE - -public: - nsXPCComponents_utils_Sandbox(); - virtual ~nsXPCComponents_utils_Sandbox(); - -private: - static nsresult CallOrConstruct(nsIXPConnectWrappedNative *wrapper, - JSContext *cx, HandleObject obj, - const CallArgs &args, bool *_retval); -}; - class nsXPCComponents_Utils : public nsIXPCComponents_Utils, public nsIXPCScriptable, @@ -2649,10 +2625,9 @@ NS_IMETHODIMP nsXPCComponents_Utils::GetSandbox(nsIXPCComponents_utils_Sandbox **aSandbox) { NS_ENSURE_ARG_POINTER(aSandbox); - if (!mSandbox && !(mSandbox = new nsXPCComponents_utils_Sandbox())) { - *aSandbox = nullptr; - return NS_ERROR_OUT_OF_MEMORY; - } + if (!mSandbox) + mSandbox = NewSandboxConstructor(); + NS_ADDREF(*aSandbox = mSandbox); return NS_OK; } @@ -2793,1344 +2768,6 @@ nsXPCComponents_Utils::ReportError(const JS::Value &errorArg, JSContext *cx) return NS_OK; } -#include "nsIScriptSecurityManager.h" -#include "nsIURI.h" -#include "nsNetUtil.h" -const char kScriptSecurityManagerContractID[] = NS_SCRIPTSECURITYMANAGER_CONTRACTID; - -NS_IMPL_ISUPPORTS3(SandboxPrivate, - nsIScriptObjectPrincipal, - nsIGlobalObject, - nsISupportsWeakReference) - -static bool -SandboxDump(JSContext *cx, unsigned argc, jsval *vp) -{ - JSString *str; - if (!argc) - return true; - - str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]); - if (!str) - return false; - - size_t length; - const jschar *chars = JS_GetStringCharsZAndLength(cx, str, &length); - if (!chars) - return false; - - nsDependentString wstr(chars, length); - char *cstr = ToNewUTF8String(wstr); - if (!cstr) - return false; - -#if defined(XP_MACOSX) - // Be nice and convert all \r to \n. - char *c = cstr, *cEnd = cstr + strlen(cstr); - while (c < cEnd) { - if (*c == '\r') - *c = '\n'; - c++; - } -#endif - - fputs(cstr, stdout); - fflush(stdout); - NS_Free(cstr); - JS_SET_RVAL(cx, vp, JSVAL_TRUE); - return true; -} - -static bool -SandboxDebug(JSContext *cx, unsigned argc, jsval *vp) -{ -#ifdef DEBUG - return SandboxDump(cx, argc, vp); -#else - return true; -#endif -} - -static bool -SandboxImport(JSContext *cx, unsigned argc, Value *vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - if (args.length() < 1 || args[0].isPrimitive()) { - XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx); - return false; - } - - RootedString funname(cx); - if (args.length() > 1) { - // Use the second parameter as the function name. - funname = JS_ValueToString(cx, args[1]); - if (!funname) - return false; - } else { - // NB: funobj must only be used to get the JSFunction out. - RootedObject funobj(cx, &args[0].toObject()); - if (js::IsProxy(funobj)) { - funobj = XPCWrapper::UnsafeUnwrapSecurityWrapper(funobj); - } - - JSAutoCompartment ac(cx, funobj); - - JSFunction *fun = JS_ValueToFunction(cx, ObjectValue(*funobj)); - if (!fun) { - XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx); - return false; - } - - // Use the actual function name as the name. - funname = JS_GetFunctionId(fun); - if (!funname) { - XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx); - return false; - } - } - - RootedId id(cx); - if (!JS_ValueToId(cx, StringValue(funname), id.address())) - return false; - - // We need to resolve the this object, because this function is used - // unbound and should still work and act on the original sandbox. - RootedObject thisObject(cx, JS_THIS_OBJECT(cx, vp)); - if (!thisObject) { - XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx); - return false; - } - if (!JS_SetPropertyById(cx, thisObject, id, args[0])) - return false; - - args.rval().setUndefined(); - return true; -} - -static bool -CreateXMLHttpRequest(JSContext *cx, unsigned argc, jsval *vp) -{ - nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); - if (!ssm) - return false; - - nsIPrincipal *subjectPrincipal = ssm->GetCxSubjectPrincipal(cx); - if (!subjectPrincipal) - return false; - - RootedObject global(cx, JS::CurrentGlobalOrNull(cx)); - MOZ_ASSERT(global); - - nsIScriptObjectPrincipal *sop = - static_cast(xpc_GetJSPrivate(global)); - nsCOMPtr iglobal = do_QueryInterface(sop); - - nsCOMPtr xhr = new nsXMLHttpRequest(); - nsresult rv = xhr->Init(subjectPrincipal, nullptr, iglobal, nullptr); - if (NS_FAILED(rv)) - return false; - - rv = nsContentUtils::WrapNative(cx, global, xhr, vp); - if (NS_FAILED(rv)) - return false; - - return true; -} - -bool -NewFunctionForwarder(JSContext *cx, HandleId id, HandleObject callable, - bool doclone, MutableHandleValue vp); - -/* - * Instead of simply wrapping a function into another compartment, - * this helper function creates a native function in the target - * compartment and forwards the call to the original function. - * That call will be different than a regular JS function call in - * that, the |this| is left unbound, and all the non-native JS - * object arguments will be cloned using the structured clone - * algorithm. - * The return value is the new forwarder function, wrapped into - * the caller's compartment. - * The 3rd argument is the name of the property that will - * be set on the target scope, with the forwarder function as - * the value. - * The principal of the caller must subsume that of the target. - * - * Expected type of the arguments and the return value: - * function exportFunction(function funToExport, - * object targetScope, - * string name) - */ -static bool -ExportFunction(JSContext *cx, unsigned argc, jsval *vp) -{ - MOZ_ASSERT(cx); - if (argc < 3) { - JS_ReportError(cx, "Function requires at least 3 arguments"); - return false; - } - - CallArgs args = CallArgsFromVp(argc, vp); - if (!args[0].isObject() || !args[1].isObject() || !args[2].isString()) { - JS_ReportError(cx, "Invalid argument"); - return false; - } - - RootedObject funObj(cx, &args[0].toObject()); - RootedObject targetScope(cx, &args[1].toObject()); - RootedString funName(cx, args[2].toString()); - - // We can only export functions to scopes those are transparent for us, - // so if there is a security wrapper around targetScope we must throw. - targetScope = CheckedUnwrap(targetScope); - if (!targetScope) { - JS_ReportError(cx, "Permission denied to export function into scope"); - return false; - } - - if (JS_GetStringLength(funName) == 0) { - JS_ReportError(cx, "3rd argument should be a non-empty string"); - return false; - } - - { - // We need to operate in the target scope from here on, let's enter - // its compartment. - JSAutoCompartment ac(cx, targetScope); - - // Unwrapping to see if we have a callable. - funObj = UncheckedUnwrap(funObj); - if (!JS_ObjectIsCallable(cx, funObj)) { - JS_ReportError(cx, "First argument must be a function"); - return false; - } - - // The function forwarder will live in the target compartment. Since - // this function will be referenced from its private slot, to avoid a - // GC hazard, we must wrap it to the same compartment. - if (!JS_WrapObject(cx, funObj.address())) - return false; - - RootedId id(cx); - if (!JS_ValueToId(cx, args[2], id.address())) - return false; - - // And now, let's create the forwarder function in the target compartment - // for the function the be exported. - if (!NewFunctionForwarder(cx, id, funObj, /* doclone = */ true, args.rval())) { - JS_ReportError(cx, "Exporting function failed"); - return false; - } - - // We have the forwarder function in the target compartment, now - // we have to add it to the target scope as a property. - if (!JS_DefinePropertyById(cx, targetScope, id, args.rval(), - JS_PropertyStub, JS_StrictPropertyStub, - JSPROP_ENUMERATE)) - return false; - } - - // Finally we have to re-wrap the exported function back to the caller compartment. - if (!JS_WrapValue(cx, args.rval().address())) - return false; - - return true; -} - -static bool -GetFilenameAndLineNumber(JSContext *cx, nsACString &filename, unsigned &lineno) -{ - JSScript *script; - if (JS_DescribeScriptedCaller(cx, &script, &lineno)) { - if (const char *cfilename = JS_GetScriptFilename(cx, script)) { - filename.Assign(nsDependentCString(cfilename)); - return true; - } - } - return false; -} - -namespace xpc { -bool -IsReflector(JSObject *obj) -{ - return IS_WN_REFLECTOR(obj) || dom::IsDOMObject(obj); -} -} /* namespace xpc */ - -enum ForwarderCloneTags { - SCTAG_BASE = JS_SCTAG_USER_MIN, - SCTAG_REFLECTOR -}; - -static JSObject * -CloneNonReflectorsRead(JSContext *cx, JSStructuredCloneReader *reader, uint32_t tag, - uint32_t data, void *closure) -{ - MOZ_ASSERT(closure, "Null pointer!"); - AutoObjectVector *reflectors = static_cast(closure); - if (tag == SCTAG_REFLECTOR) { - MOZ_ASSERT(!data); - - size_t idx; - if (JS_ReadBytes(reader, &idx, sizeof(size_t))) { - RootedObject reflector(cx, reflectors->handleAt(idx)); - MOZ_ASSERT(reflector, "No object pointer?"); - MOZ_ASSERT(IsReflector(reflector), "Object pointer must be a reflector!"); - - JS_WrapObject(cx, reflector.address()); - JS_ASSERT(WrapperFactory::IsXrayWrapper(reflector) || - IsReflector(reflector)); - - return reflector; - } - } - - JS_ReportError(cx, "CloneNonReflectorsRead error"); - return nullptr; -} - -static bool -CloneNonReflectorsWrite(JSContext *cx, JSStructuredCloneWriter *writer, - Handle obj, void *closure) -{ - MOZ_ASSERT(closure, "Null pointer!"); - - // We need to maintain a list of reflectors to make sure all these objects - // are properly rooter. Only their indices will be serialized. - AutoObjectVector *reflectors = static_cast(closure); - if (IsReflector(obj)) { - if (!reflectors->append(obj)) - return false; - - size_t idx = reflectors->length()-1; - if (JS_WriteUint32Pair(writer, SCTAG_REFLECTOR, 0) && - JS_WriteBytes(writer, &idx, sizeof(size_t))) { - return true; - } - } - - JS_ReportError(cx, "CloneNonReflectorsWrite error"); - return false; -} - -JSStructuredCloneCallbacks gForwarderStructuredCloneCallbacks = { - CloneNonReflectorsRead, - CloneNonReflectorsWrite, - nullptr -}; - -/* - * This is a special structured cloning, that clones only non-reflectors. - * The function assumes the cx is already entered the compartment we want - * to clone to, and that if val is an object is from the compartment we - * clone from. - */ -bool -CloneNonReflectors(JSContext *cx, MutableHandleValue val) -{ - JSAutoStructuredCloneBuffer buffer; - AutoObjectVector rootedReflectors(cx); - { - // For parsing val we have to enter its compartment. - // (unless it's a primitive) - Maybe ac; - if (val.isObject()) { - ac.construct(cx, &val.toObject()); - } - - if (!buffer.write(cx, val, - &gForwarderStructuredCloneCallbacks, - &rootedReflectors)) - { - return false; - } - } - - // Now recreate the clones in the target compartment. - RootedValue rval(cx); - if (!buffer.read(cx, val.address(), - &gForwarderStructuredCloneCallbacks, - &rootedReflectors)) - { - return false; - } - - return true; -} - -/* - * Similar to evalInSandbox except this one is used to eval a script in the - * scope of a window. Also note, that the return value and the possible exceptions - * in the script are structured cloned, unless they are natives (then they are just - * wrapped). - * Principal of the caller must subsume the target's. - * - * Expected type of the arguments: - * value evalInWindow(string script, - * object window) - */ -static bool -EvalInWindow(JSContext *cx, unsigned argc, jsval *vp) -{ - MOZ_ASSERT(cx); - if (argc < 2) { - JS_ReportError(cx, "Function requires two arguments"); - return false; - } - - CallArgs args = CallArgsFromVp(argc, vp); - if (!args[0].isString() || !args[1].isObject()) { - JS_ReportError(cx, "Invalid arguments"); - return false; - } - - RootedString srcString(cx, args[0].toString()); - RootedObject targetScope(cx, &args[1].toObject()); - - // If we cannot unwrap we must not eval in it. - targetScope = CheckedUnwrap(targetScope); - if (!targetScope) { - JS_ReportError(cx, "Permission denied to eval in target scope"); - return false; - } - - // Make sure that we have a window object. - RootedObject inner(cx, CheckedUnwrap(targetScope, /* stopAtOuter = */ false)); - nsCOMPtr global; - nsCOMPtr window; - if (!JS_IsGlobalObject(inner) || - !(global = GetNativeForGlobal(inner)) || - !(window = do_QueryInterface(global))) - { - JS_ReportError(cx, "Second argument must be a window"); - return false; - } - - nsCOMPtr context = - (static_cast(window.get()))->GetScriptContext(); - if (!context) { - JS_ReportError(cx, "Script context needed"); - return false; - } - - if (!context->GetScriptsEnabled()) { - JS_ReportError(cx, "Scripts are disabled in this window"); - return false; - } - - nsCString filename; - unsigned lineNo; - if (!GetFilenameAndLineNumber(cx, filename, lineNo)) { - // Default values for non-scripted callers. - filename.Assign("Unknown"); - lineNo = 0; - } - - nsDependentJSString srcDepString; - srcDepString.init(cx, srcString); - - { - // CompileOptions must be created from the context - // we will execute this script in. - JSContext *wndCx = context->GetNativeContext(); - AutoCxPusher pusher(wndCx); - JS::CompileOptions compileOptions(wndCx); - compileOptions.setFileAndLine(filename.get(), lineNo); - - // We don't want the JS engine to automatically report - // uncaught exceptions. - nsJSUtils::EvaluateOptions evaluateOptions; - evaluateOptions.setReportUncaught(false); - - nsresult rv = nsJSUtils::EvaluateString(wndCx, - srcDepString, - targetScope, - compileOptions, - evaluateOptions, - args.rval().address()); - - if (NS_FAILED(rv)) { - // If there was an exception we get it as a return value, if - // the evaluation failed for some other reason, then a default - // exception is raised. - MOZ_ASSERT(!JS_IsExceptionPending(wndCx), - "Exception should be delivered as return value."); - if (args.rval().isUndefined()) { - MOZ_ASSERT(rv == NS_ERROR_OUT_OF_MEMORY); - return false; - } - - // If there was an exception thrown we should set it - // on the calling context. - RootedValue exn(wndCx, args.rval()); - // First we should reset the return value. - args.rval().set(UndefinedValue()); - - // Then clone the exception. - if (CloneNonReflectors(cx, &exn)) - JS_SetPendingException(cx, exn); - - return false; - } - } - - // Let's clone the return value back to the callers compartment. - if (!CloneNonReflectors(cx, args.rval())) { - args.rval().set(UndefinedValue()); - return false; - } - - return true; -} - -static bool -sandbox_enumerate(JSContext *cx, HandleObject obj) -{ - return JS_EnumerateStandardClasses(cx, obj); -} - -static bool -sandbox_resolve(JSContext *cx, HandleObject obj, HandleId id) -{ - bool resolved; - return JS_ResolveStandardClass(cx, obj, id, &resolved); -} - -static void -sandbox_finalize(JSFreeOp *fop, JSObject *obj) -{ - nsIScriptObjectPrincipal *sop = - static_cast(xpc_GetJSPrivate(obj)); - MOZ_ASSERT(sop); - static_cast(sop)->ForgetGlobalObject(); - NS_IF_RELEASE(sop); - DestroyProtoAndIfaceCache(obj); -} - -static bool -sandbox_convert(JSContext *cx, HandleObject obj, JSType type, MutableHandleValue vp) -{ - if (type == JSTYPE_OBJECT) { - vp.set(OBJECT_TO_JSVAL(obj)); - return true; - } - - return JS_ConvertStub(cx, obj, type, vp); -} - -static JSClass SandboxClass = { - "Sandbox", - XPCONNECT_GLOBAL_FLAGS, - JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, - sandbox_enumerate, sandbox_resolve, sandbox_convert, sandbox_finalize, - NULL, NULL, NULL, NULL, TraceXPCGlobal -}; - -static const JSFunctionSpec SandboxFunctions[] = { - JS_FS("dump", SandboxDump, 1,0), - JS_FS("debug", SandboxDebug, 1,0), - JS_FS("importFunction", SandboxImport, 1,0), - JS_FS_END -}; - -/***************************************************************************/ -nsXPCComponents_utils_Sandbox::nsXPCComponents_utils_Sandbox() -{ -} - -nsXPCComponents_utils_Sandbox::~nsXPCComponents_utils_Sandbox() -{ -} - -NS_INTERFACE_MAP_BEGIN(nsXPCComponents_utils_Sandbox) - NS_INTERFACE_MAP_ENTRY(nsIXPCComponents_utils_Sandbox) - NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable) - NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_utils_Sandbox) -NS_INTERFACE_MAP_END_THREADSAFE - -NS_IMPL_ADDREF(nsXPCComponents_utils_Sandbox) -NS_IMPL_RELEASE(nsXPCComponents_utils_Sandbox) - -// We use the nsIXPScriptable macros to generate lots of stuff for us. -#define XPC_MAP_CLASSNAME nsXPCComponents_utils_Sandbox -#define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_utils_Sandbox" -#define XPC_MAP_WANT_CALL -#define XPC_MAP_WANT_CONSTRUCT -#define XPC_MAP_FLAGS 0 -#include "xpc_map_end.h" /* This #undef's the above. */ - -xpc::SandboxProxyHandler xpc::sandboxProxyHandler; - -bool -xpc::IsSandboxPrototypeProxy(JSObject *obj) -{ - return js::IsProxy(obj) && - js::GetProxyHandler(obj) == &xpc::sandboxProxyHandler; -} - -bool -xpc::SandboxCallableProxyHandler::call(JSContext *cx, JS::Handle proxy, - const JS::CallArgs &args) -{ - // We forward the call to our underlying callable. - - // The parent of our proxy is the SandboxProxyHandler proxy - RootedObject sandboxProxy(cx, JS_GetParent(proxy)); - MOZ_ASSERT(js::IsProxy(sandboxProxy) && - js::GetProxyHandler(sandboxProxy) == &xpc::sandboxProxyHandler); - - // The parent of the sandboxProxy is the sandbox global, and the - // target object is the original proto. - RootedObject sandboxGlobal(cx, JS_GetParent(sandboxProxy)); - MOZ_ASSERT(js::GetObjectJSClass(sandboxGlobal) == &SandboxClass); - - // If our this object is the sandbox global, we call with this set to the - // original proto instead. - // - // There are two different ways we can compute |this|. If we use - // JS_THIS_VALUE, we'll get the bonafide |this| value as passed by the - // caller, which may be undefined if a global function was invoked without - // an explicit invocant. If we use JS_THIS or JS_THIS_OBJECT, the |this| - // in |vp| will be coerced to the global, which is not the correct - // behavior in ES5 strict mode. And we have no way to compute strictness - // here. - // - // The naive approach is simply to use JS_THIS_VALUE here. If |this| was - // explicit, we can remap it appropriately. If it was implicit, then we - // leave it as undefined, and let the callee sort it out. Since the callee - // is generally in the same compartment as its global (eg the Window's - // compartment, not the Sandbox's), the callee will generally compute the - // correct |this|. - // - // However, this breaks down in the Xray case. If the sandboxPrototype - // is an Xray wrapper, then we'll end up reifying the native methods in - // the Sandbox's scope, which means that they'll compute |this| to be the - // Sandbox, breaking old-style XPC_WN_CallMethod methods. - // - // Luckily, the intent of Xrays is to provide a vanilla view of a foreign - // DOM interface, which means that we don't care about script-enacted - // strictness in the prototype's home compartment. Indeed, since DOM - // methods are always non-strict, we can just assume non-strict semantics - // if the sandboxPrototype is an Xray Wrapper, which lets us appropriately - // remap |this|. - JS::Value thisVal = - WrapperFactory::IsXrayWrapper(sandboxProxy) ? args.computeThis(cx) : args.thisv(); - if (thisVal == ObjectValue(*sandboxGlobal)) { - thisVal = ObjectValue(*js::GetProxyTargetObject(sandboxProxy)); - } - - return JS::Call(cx, thisVal, js::GetProxyPrivate(proxy), args.length(), args.array(), - args.rval()); -} - -xpc::SandboxCallableProxyHandler xpc::sandboxCallableProxyHandler; - -// Wrap a callable such that if we're called with oldThisObj as the -// "this" we will instead call it with newThisObj as the this. -static JSObject* -WrapCallable(JSContext *cx, JSObject *callable, JSObject *sandboxProtoProxy) -{ - MOZ_ASSERT(JS_ObjectIsCallable(cx, callable)); - // Our proxy is wrapping the callable. So we need to use the - // callable as the private. We use the given sandboxProtoProxy as - // the parent, and our call() hook depends on that. - MOZ_ASSERT(js::IsProxy(sandboxProtoProxy) && - js::GetProxyHandler(sandboxProtoProxy) == - &xpc::sandboxProxyHandler); - - RootedValue priv(cx, ObjectValue(*callable)); - return js::NewProxyObject(cx, &xpc::sandboxCallableProxyHandler, - priv, nullptr, - sandboxProtoProxy, js::ProxyIsCallable); -} - -template -bool BindPropertyOp(JSContext *cx, Op &op, JSPropertyDescriptor *desc, HandleId id, - unsigned attrFlag, HandleObject sandboxProtoProxy) -{ - if (!op) { - return true; - } - - RootedObject func(cx); - if (desc->attrs & attrFlag) { - // Already an object - func = JS_FUNC_TO_DATA_PTR(JSObject *, op); - } else { - // We have an actual property op. For getters, we use 0 - // args, for setters we use 1 arg. - uint32_t args = (attrFlag == JSPROP_GETTER) ? 0 : 1; - RootedObject obj(cx, desc->obj); - func = GeneratePropertyOp(cx, obj, id, args, op); - if (!func) - return false; - } - func = WrapCallable(cx, func, sandboxProtoProxy); - if (!func) - return false; - op = JS_DATA_TO_FUNC_PTR(Op, func.get()); - desc->attrs |= attrFlag; - return true; -} - -extern bool -XPC_WN_Helper_GetProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp); -extern bool -XPC_WN_Helper_SetProperty(JSContext *cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp); - -bool -xpc::SandboxProxyHandler::getPropertyDescriptor(JSContext *cx, - JS::Handle proxy, - JS::Handle id, - JS::MutableHandle desc, - unsigned flags) -{ - JS::RootedObject obj(cx, wrappedObject(proxy)); - - MOZ_ASSERT(js::GetObjectCompartment(obj) == js::GetObjectCompartment(proxy)); - if (!JS_GetPropertyDescriptorById(cx, obj, id, - flags, desc)) - return false; - - if (!desc.object()) - return true; // No property, nothing to do - - // Now fix up the getter/setter/value as needed to be bound to desc->obj - // Don't mess with holder_get and holder_set, though, because those rely on - // the "vp is prefilled with the value in the slot" behavior that property - // ops can in theory rely on, but our property op forwarder doesn't know how - // to make that happen. Since we really only need to rebind the DOM methods - // here, not rebindings holder_get and holder_set is OK. - // - // Similarly, don't mess with XPC_WN_Helper_GetProperty and - // XPC_WN_Helper_SetProperty, for the same reasons: that could confuse our - // access to expandos when we're not doing Xrays. - if (desc.getter() != xpc::holder_get && - desc.getter() != XPC_WN_Helper_GetProperty && - !BindPropertyOp(cx, desc.getter(), desc.address(), id, JSPROP_GETTER, proxy)) - return false; - if (desc.setter() != xpc::holder_set && - desc.setter() != XPC_WN_Helper_SetProperty && - !BindPropertyOp(cx, desc.setter(), desc.address(), id, JSPROP_SETTER, proxy)) - return false; - if (desc.value().isObject()) { - JSObject* val = &desc.value().toObject(); - if (JS_ObjectIsCallable(cx, val)) { - val = WrapCallable(cx, val, proxy); - if (!val) - return false; - desc.value().setObject(*val); - } - } - - return true; -} - -bool -xpc::SandboxProxyHandler::getOwnPropertyDescriptor(JSContext *cx, - JS::Handle proxy, - JS::Handle id, - JS::MutableHandle desc, - unsigned flags) -{ - if (!getPropertyDescriptor(cx, proxy, id, desc, flags)) - return false; - - if (desc.object() != wrappedObject(proxy)) - desc.object().set(nullptr); - - return true; -} - -/* - * Reuse the BaseProxyHandler versions of the derived traps that are implemented - * in terms of the fundamental traps. - */ - -bool -xpc::SandboxProxyHandler::has(JSContext *cx, JS::Handle proxy, - JS::Handle id, bool *bp) -{ - return BaseProxyHandler::has(cx, proxy, id, bp); -} -bool -xpc::SandboxProxyHandler::hasOwn(JSContext *cx, JS::Handle proxy, - JS::Handle id, bool *bp) -{ - return BaseProxyHandler::hasOwn(cx, proxy, id, bp); -} - -bool -xpc::SandboxProxyHandler::get(JSContext *cx, JS::Handle proxy, - JS::Handle receiver, - JS::Handle id, - JS::MutableHandle vp) -{ - return BaseProxyHandler::get(cx, proxy, receiver, id, vp); -} - -bool -xpc::SandboxProxyHandler::set(JSContext *cx, JS::Handle proxy, - JS::Handle receiver, - JS::Handle id, - bool strict, - JS::MutableHandle vp) -{ - return BaseProxyHandler::set(cx, proxy, receiver, id, strict, vp); -} - -bool -xpc::SandboxProxyHandler::keys(JSContext *cx, JS::Handle proxy, - AutoIdVector &props) -{ - return BaseProxyHandler::keys(cx, proxy, props); -} - -bool -xpc::SandboxProxyHandler::iterate(JSContext *cx, JS::Handle proxy, - unsigned flags, JS::MutableHandle vp) -{ - return BaseProxyHandler::iterate(cx, proxy, flags, vp); -} - -nsresult -xpc_CreateSandboxObject(JSContext *cx, jsval *vp, nsISupports *prinOrSop, SandboxOptions& options) -{ - // Create the sandbox global object - nsresult rv; - nsCOMPtr xpc(do_GetService(nsIXPConnect::GetCID(), &rv)); - if (NS_FAILED(rv)) - return NS_ERROR_XPC_UNEXPECTED; - - nsCOMPtr principal = do_QueryInterface(prinOrSop); - if (!principal) { - nsCOMPtr sop = do_QueryInterface(prinOrSop); - if (sop) { - principal = sop->GetPrincipal(); - } else { - principal = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv); - MOZ_ASSERT(NS_FAILED(rv) || principal, "Bad return from do_CreateInstance"); - - if (!principal || NS_FAILED(rv)) { - if (NS_SUCCEEDED(rv)) { - rv = NS_ERROR_FAILURE; - } - - return rv; - } - } - MOZ_ASSERT(principal); - } - - JS::CompartmentOptions compartmentOptions; - compartmentOptions.setZone(options.sameZoneAs - ? JS::SameZoneAs(js::UncheckedUnwrap(options.sameZoneAs)) - : JS::SystemZone); - RootedObject sandbox(cx, xpc::CreateGlobalObject(cx, &SandboxClass, - principal, compartmentOptions)); - if (!sandbox) - return NS_ERROR_FAILURE; - - // Set up the wantXrays flag, which indicates whether xrays are desired even - // for same-origin access. - // - // This flag has historically been ignored for chrome sandboxes due to - // quirks in the wrapping implementation that have now been removed. Indeed, - // same-origin Xrays for chrome->chrome access seems a bit superfluous. - // Arguably we should just flip the default for chrome and still honor the - // flag, but such a change would break code in subtle ways for minimal - // benefit. So we just switch it off here. - xpc::GetCompartmentPrivate(sandbox)->wantXrays = - AccessCheck::isChrome(sandbox) ? false : options.wantXrays; - - { - JSAutoCompartment ac(cx, sandbox); - - if (options.proto) { - bool ok = JS_WrapObject(cx, options.proto.address()); - if (!ok) - return NS_ERROR_XPC_UNEXPECTED; - - if (xpc::WrapperFactory::IsXrayWrapper(options.proto) && !options.wantXrays) { - RootedValue v(cx, ObjectValue(*options.proto)); - if (!xpc::WrapperFactory::WaiveXrayAndWrap(cx, v.address())) - return NS_ERROR_FAILURE; - options.proto = &v.toObject(); - } - - // Now check what sort of thing we've got in |proto| - JSObject *unwrappedProto = js::UncheckedUnwrap(options.proto, false); - js::Class *unwrappedClass = js::GetObjectClass(unwrappedProto); - if (IS_WN_CLASS(unwrappedClass) || - mozilla::dom::IsDOMClass(Jsvalify(unwrappedClass))) { - // Wrap it up in a proxy that will do the right thing in terms - // of this-binding for methods. - RootedValue priv(cx, ObjectValue(*options.proto)); - options.proto = js::NewProxyObject(cx, &xpc::sandboxProxyHandler, - priv, nullptr, sandbox); - if (!options.proto) - return NS_ERROR_OUT_OF_MEMORY; - } - - ok = JS_SetPrototype(cx, sandbox, options.proto); - if (!ok) - return NS_ERROR_XPC_UNEXPECTED; - } - - nsCOMPtr sbp = - new SandboxPrivate(principal, sandbox); - - // Pass on ownership of sbp to |sandbox|. - JS_SetPrivate(sandbox, sbp.forget().get()); - - bool allowComponents = nsContentUtils::IsSystemPrincipal(principal) || - nsContentUtils::IsExpandedPrincipal(principal); - if (options.wantComponents && allowComponents && - !nsXPCComponents::AttachComponentsObject(cx, GetObjectScope(sandbox))) - return NS_ERROR_XPC_UNEXPECTED; - - if (!XPCNativeWrapper::AttachNewConstructorObject(cx, sandbox)) - return NS_ERROR_XPC_UNEXPECTED; - - if (!JS_DefineFunctions(cx, sandbox, SandboxFunctions)) - return NS_ERROR_XPC_UNEXPECTED; - - if (options.wantXHRConstructor && - !JS_DefineFunction(cx, sandbox, "XMLHttpRequest", CreateXMLHttpRequest, 0, JSFUN_CONSTRUCTOR)) - return NS_ERROR_XPC_UNEXPECTED; - - if (options.wantExportHelpers && - (!JS_DefineFunction(cx, sandbox, "exportFunction", ExportFunction, 3, 0) || - !JS_DefineFunction(cx, sandbox, "evalInWindow", EvalInWindow, 2, 0))) - return NS_ERROR_XPC_UNEXPECTED; - - } - - if (vp) { - // We have this crazy behavior where wantXrays=false also implies that the - // returned sandbox is implicitly waived. We've stopped advertising it, but - // keep supporting it for now. - *vp = OBJECT_TO_JSVAL(sandbox); - if (options.wantXrays && !JS_WrapValue(cx, vp)) - return NS_ERROR_UNEXPECTED; - if (!options.wantXrays && !xpc::WrapperFactory::WaiveXrayAndWrap(cx, vp)) - return NS_ERROR_UNEXPECTED; - } - - // Set the location information for the new global, so that tools like - // about:memory may use that information - xpc::SetLocationForGlobal(sandbox, options.sandboxName); - - JS_FireOnNewGlobalObject(cx, sandbox); - - return NS_OK; -} - -/* bool call(in nsIXPConnectWrappedNative wrapper, - in JSContextPtr cx, - in JSObjectPtr obj, - in uint32_t argc, - in JSValPtr argv, - in JSValPtr vp); -*/ -NS_IMETHODIMP -nsXPCComponents_utils_Sandbox::Call(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *objArg, const CallArgs &args, bool *_retval) -{ - RootedObject obj(cx, objArg); - return CallOrConstruct(wrapper, cx, obj, args, _retval); -} - -/* bool construct(in nsIXPConnectWrappedNative wrapper, - in JSContextPtr cx, - in JSObjectPtr obj, - in uint32_t argc, - in JSValPtr argv, - in JSValPtr vp); -*/ -NS_IMETHODIMP -nsXPCComponents_utils_Sandbox::Construct(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *objArg, const CallArgs &args, bool *_retval) -{ - RootedObject obj(cx, objArg); - return CallOrConstruct(wrapper, cx, obj, args, _retval); -} - -// for sandbox constructor the first argument can be a URI string in which case -// we use the related Codebase Principal for the sandbox -nsresult -GetPrincipalFromString(JSContext *cx, HandleString codebase, nsIPrincipal **principal) -{ - MOZ_ASSERT(principal); - MOZ_ASSERT(codebase); - nsCOMPtr uri; - nsDependentJSString codebaseStr; - NS_ENSURE_TRUE(codebaseStr.init(cx, codebase), NS_ERROR_FAILURE); - nsresult rv = NS_NewURI(getter_AddRefs(uri), codebaseStr); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr secman = - do_GetService(kScriptSecurityManagerContractID); - NS_ENSURE_TRUE(secman, NS_ERROR_FAILURE); - - // We could allow passing in the app-id and browser-element info to the - // sandbox constructor. But creating a sandbox based on a string is a - // deprecated API so no need to add features to it. - rv = secman->GetNoAppCodebasePrincipal(uri, principal); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(*principal, NS_ERROR_FAILURE); - - return NS_OK; -} - -// for sandbox constructor the first argument can be a principal object or -// a script object principal (Document, Window) -nsresult -GetPrincipalOrSOP(JSContext *cx, HandleObject from, nsISupports **out) -{ - MOZ_ASSERT(out); - *out = NULL; - - nsXPConnect* xpc = nsXPConnect::XPConnect(); - nsCOMPtr wrapper; - xpc->GetWrappedNativeOfJSObject(cx, from, - getter_AddRefs(wrapper)); - - NS_ENSURE_TRUE(wrapper, NS_ERROR_INVALID_ARG); - - if (nsCOMPtr sop = do_QueryWrappedNative(wrapper)) { - sop.forget(out); - return NS_OK; - } - - nsCOMPtr principal = do_QueryWrappedNative(wrapper); - principal.forget(out); - NS_ENSURE_TRUE(*out, NS_ERROR_INVALID_ARG); - - return NS_OK; -} - -// the first parameter of the sandbox constructor might be an array of principals, either in string -// format or actual objects (see GetPrincipalOrSOP) -nsresult -GetExpandedPrincipal(JSContext *cx, HandleObject arrayObj, nsIExpandedPrincipal **out) -{ - MOZ_ASSERT(out); - uint32_t length; - - if (!JS_IsArrayObject(cx, arrayObj) || - !JS_GetArrayLength(cx, arrayObj, &length) || - !length) - { - // we need a white list of principals or uri strings to create an - // expanded principal, if we got an empty array or something else - // report error - return NS_ERROR_INVALID_ARG; - } - - nsTArray< nsCOMPtr > allowedDomains(length); - allowedDomains.SetLength(length); - nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); - NS_ENSURE_TRUE(ssm, NS_ERROR_XPC_UNEXPECTED); - - for (uint32_t i = 0; i < length; ++i) { - RootedValue allowed(cx); - if (!JS_GetElement(cx, arrayObj, i, &allowed)) - return NS_ERROR_INVALID_ARG; - - nsresult rv; - nsCOMPtr principal; - if (allowed.isString()) { - // in case of string let's try to fetch a codebase principal from it - RootedString str(cx, allowed.toString()); - rv = GetPrincipalFromString(cx, str, getter_AddRefs(principal)); - NS_ENSURE_SUCCESS(rv, rv); - } else if (allowed.isObject()) { - // in case of object let's see if it's a Principal or a ScriptObjectPrincipal - nsCOMPtr prinOrSop; - RootedObject obj(cx, &allowed.toObject()); - rv = GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr sop(do_QueryInterface(prinOrSop)); - principal = do_QueryInterface(prinOrSop); - if (sop) { - principal = sop->GetPrincipal(); - } - } - NS_ENSURE_TRUE(principal, NS_ERROR_INVALID_ARG); - - // We do not allow ExpandedPrincipals to contain any system principals - bool isSystem; - rv = ssm->IsSystemPrincipal(principal, &isSystem); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_FALSE(isSystem, NS_ERROR_INVALID_ARG); - allowedDomains[i] = principal; - } - - nsCOMPtr result = new nsExpandedPrincipal(allowedDomains); - result.forget(out); - return NS_OK; -} - -// helper that tries to get a property form the options object -nsresult -GetPropFromOptions(JSContext *cx, HandleObject from, const char *name, MutableHandleValue prop, - bool *found) -{ - if (!JS_HasProperty(cx, from, name, found)) - return NS_ERROR_INVALID_ARG; - - if (found && !JS_GetProperty(cx, from, name, prop)) - return NS_ERROR_INVALID_ARG; - - return NS_OK; -} - -// helper that tries to get a boolean property form the options object -nsresult -GetBoolPropFromOptions(JSContext *cx, HandleObject from, const char *name, bool *prop) -{ - MOZ_ASSERT(prop); - - - RootedValue value(cx); - bool found; - if (NS_FAILED(GetPropFromOptions(cx, from, name, &value, &found))) - return NS_ERROR_INVALID_ARG; - - if (!found) - return NS_OK; - - if (!value.isBoolean()) - return NS_ERROR_INVALID_ARG; - - *prop = value.toBoolean(); - return NS_OK; -} - -// helper that tries to get an object property form the options object -nsresult -GetObjPropFromOptions(JSContext *cx, HandleObject from, const char *name, JSObject **prop) -{ - MOZ_ASSERT(prop); - - RootedValue value(cx); - bool found; - if (NS_FAILED(GetPropFromOptions(cx, from, name, &value, &found))) - return NS_ERROR_INVALID_ARG; - - if (!found) { - *prop = NULL; - return NS_OK; - } - - if (!value.isObject()) - return NS_ERROR_INVALID_ARG; - - *prop = &value.toObject(); - return NS_OK; -} - -// helper that tries to get a string property form the options object -nsresult -GetStringPropFromOptions(JSContext *cx, HandleObject from, const char *name, nsCString &prop) -{ - RootedValue value(cx); - bool found; - nsresult rv = GetPropFromOptions(cx, from, name, &value, &found); - NS_ENSURE_SUCCESS(rv, rv); - - if (!found) - return NS_OK; - - NS_ENSURE_TRUE(value.isString(), NS_ERROR_INVALID_ARG); - - char *tmp = JS_EncodeString(cx, value.toString()); - NS_ENSURE_TRUE(tmp, NS_ERROR_INVALID_ARG); - prop.Adopt(tmp, strlen(tmp)); - return NS_OK; -} - -// helper that parsing the sandbox options object (from) and sets the fields of the incoming options struct (options) -nsresult -ParseOptionsObject(JSContext *cx, jsval from, SandboxOptions &options) -{ - NS_ENSURE_TRUE(from.isObject(), NS_ERROR_INVALID_ARG); - RootedObject optionsObject(cx, &from.toObject()); - nsresult rv = GetObjPropFromOptions(cx, optionsObject, - "sandboxPrototype", options.proto.address()); - NS_ENSURE_SUCCESS(rv, rv); - - rv = GetBoolPropFromOptions(cx, optionsObject, - "wantXrays", &options.wantXrays); - NS_ENSURE_SUCCESS(rv, rv); - - rv = GetBoolPropFromOptions(cx, optionsObject, - "wantComponents", &options.wantComponents); - NS_ENSURE_SUCCESS(rv, rv); - - rv = GetBoolPropFromOptions(cx, optionsObject, - "wantXHRConstructor", &options.wantXHRConstructor); - NS_ENSURE_SUCCESS(rv, rv); - - rv = GetBoolPropFromOptions(cx, optionsObject, - "wantExportHelpers", &options.wantExportHelpers); - NS_ENSURE_SUCCESS(rv, rv); - - rv = GetStringPropFromOptions(cx, optionsObject, - "sandboxName", options.sandboxName); - NS_ENSURE_SUCCESS(rv, rv); - - rv = GetObjPropFromOptions(cx, optionsObject, - "sameZoneAs", options.sameZoneAs.address()); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -static nsresult -AssembleSandboxMemoryReporterName(JSContext *cx, nsCString &sandboxName) -{ - // Use a default name when the caller did not provide a sandboxName. - if (sandboxName.IsEmpty()) - sandboxName = NS_LITERAL_CSTRING("[anonymous sandbox]"); - - nsXPConnect* xpc = nsXPConnect::XPConnect(); - // Get the xpconnect native call context. - nsAXPCNativeCallContext *cc = nullptr; - xpc->GetCurrentNativeCallContext(&cc); - NS_ENSURE_TRUE(cc, NS_ERROR_INVALID_ARG); - - // Get the current source info from xpc. - nsCOMPtr frame; - xpc->GetCurrentJSStack(getter_AddRefs(frame)); - - // Append the caller's location information. - if (frame) { - nsCString location; - int32_t lineNumber = 0; - frame->GetFilename(getter_Copies(location)); - frame->GetLineNumber(&lineNumber); - - sandboxName.AppendLiteral(" (from: "); - sandboxName.Append(location); - sandboxName.AppendLiteral(":"); - sandboxName.AppendInt(lineNumber); - sandboxName.AppendLiteral(")"); - } - - return NS_OK; -} - -// static -nsresult -nsXPCComponents_utils_Sandbox::CallOrConstruct(nsIXPConnectWrappedNative *wrapper, - JSContext *cx, HandleObject obj, - const CallArgs &args, bool *_retval) -{ - if (args.length() < 1) - return ThrowAndFail(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx, _retval); - - nsresult rv; - - // Make sure to set up principals on the sandbox before initing classes - nsCOMPtr principal; - nsCOMPtr expanded; - nsCOMPtr prinOrSop; - - if (args[0].isString()) { - RootedString str(cx, args[0].toString()); - rv = GetPrincipalFromString(cx, str, getter_AddRefs(principal)); - prinOrSop = principal; - } else if (args[0].isObject()) { - RootedObject obj(cx, &args[0].toObject()); - if (JS_IsArrayObject(cx, obj)) { - rv = GetExpandedPrincipal(cx, obj, getter_AddRefs(expanded)); - prinOrSop = expanded; - } else { - rv = GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop)); - } - } else { - return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); - } - - if (NS_FAILED(rv)) - return ThrowAndFail(rv, cx, _retval); - - SandboxOptions options(cx); - - if (args.length() > 1 && args[1].isObject()) { - if (NS_FAILED(ParseOptionsObject(cx, args[1], options))) - return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); - } - - if (NS_FAILED(AssembleSandboxMemoryReporterName(cx, options.sandboxName))) - return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); - - rv = xpc_CreateSandboxObject(cx, args.rval().address(), prinOrSop, options); - - if (NS_FAILED(rv)) - return ThrowAndFail(rv, cx, _retval); - - *_retval = true; - - return rv; -} - -class ContextHolder : public nsIScriptObjectPrincipal -{ -public: - ContextHolder(JSContext *aOuterCx, HandleObject aSandbox, nsIPrincipal *aPrincipal); - virtual ~ContextHolder(); - - JSContext * GetJSContext() - { - return mJSContext; - } - - nsIPrincipal * GetPrincipal() { return mPrincipal; } - - NS_DECL_ISUPPORTS - -private: - JSContext* mJSContext; - nsCOMPtr mPrincipal; -}; - -NS_IMPL_ISUPPORTS1(ContextHolder, nsIScriptObjectPrincipal) - -ContextHolder::ContextHolder(JSContext *aOuterCx, - HandleObject aSandbox, - nsIPrincipal *aPrincipal) - : mJSContext(JS_NewContext(JS_GetRuntime(aOuterCx), 1024)), - mPrincipal(aPrincipal) -{ - if (mJSContext) { - bool isChrome; - DebugOnly rv = XPCWrapper::GetSecurityManager()-> - IsSystemPrincipal(mPrincipal, &isChrome); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - - JS_SetOptions(mJSContext, - JS_GetOptions(mJSContext) | - JSOPTION_DONT_REPORT_UNCAUGHT | - JSOPTION_PRIVATE_IS_NSISUPPORTS); - js::SetDefaultObjectForContext(mJSContext, aSandbox); - JS_SetContextPrivate(mJSContext, this); - } -} - -ContextHolder::~ContextHolder() -{ - if (mJSContext) - JS_DestroyContextNoGC(mJSContext); -} - -/***************************************************************************/ - /* void evalInSandbox(in AString source, in nativeobj sandbox); */ NS_IMETHODIMP nsXPCComponents_Utils::EvalInSandbox(const nsAString& source, @@ -4203,109 +2840,6 @@ nsXPCComponents_Utils::EvalInSandbox(const nsAString& source, return NS_OK; } -nsresult -xpc_EvalInSandbox(JSContext *cx, HandleObject sandboxArg, const nsAString& source, - const char *filename, int32_t lineNo, - JSVersion jsVersion, bool returnStringOnly, MutableHandleValue rval) -{ - JS_AbortIfWrongThread(JS_GetRuntime(cx)); - rval.set(UndefinedValue()); - - bool waiveXray = xpc::WrapperFactory::HasWaiveXrayFlag(sandboxArg); - RootedObject sandbox(cx, js::CheckedUnwrap(sandboxArg)); - if (!sandbox || js::GetObjectJSClass(sandbox) != &SandboxClass) { - return NS_ERROR_INVALID_ARG; - } - - nsIScriptObjectPrincipal *sop = - (nsIScriptObjectPrincipal*)xpc_GetJSPrivate(sandbox); - MOZ_ASSERT(sop, "Invalid sandbox passed"); - nsCOMPtr prin = sop->GetPrincipal(); - NS_ENSURE_TRUE(prin, NS_ERROR_FAILURE); - - nsAutoCString filenameBuf; - if (!filename) { - // Default to the spec of the principal. - nsJSPrincipals::get(prin)->GetScriptLocation(filenameBuf); - filename = filenameBuf.get(); - lineNo = 1; - } - - // We create a separate cx to do the sandbox evaluation. Scope it. - RootedValue v(cx, UndefinedValue()); - RootedValue exn(cx, UndefinedValue()); - bool ok = true; - { - // Make a special cx for the sandbox and push it. - // NB: As soon as the RefPtr goes away, the cx goes away. So declare - // it first so that it disappears last. - nsRefPtr sandcxHolder = new ContextHolder(cx, sandbox, prin); - JSContext *sandcx = sandcxHolder->GetJSContext(); - if (!sandcx) { - JS_ReportError(cx, "Can't prepare context for evalInSandbox"); - return NS_ERROR_OUT_OF_MEMORY; - } - nsCxPusher pusher; - pusher.Push(sandcx); - JSAutoCompartment ac(sandcx, sandbox); - - JS::CompileOptions options(sandcx); - options.setPrincipals(nsJSPrincipals::get(prin)) - .setFileAndLine(filename, lineNo); - if (jsVersion != JSVERSION_DEFAULT) - options.setVersion(jsVersion); - JS::RootedObject rootedSandbox(sandcx, sandbox); - ok = JS::Evaluate(sandcx, rootedSandbox, options, - PromiseFlatString(source).get(), source.Length(), - v.address()); - if (ok && returnStringOnly && !v.isUndefined()) { - JSString *str = JS_ValueToString(sandcx, v); - ok = !!str; - v = ok ? JS::StringValue(str) : JS::UndefinedValue(); - } - - // If the sandbox threw an exception, grab it off the context. - if (JS_GetPendingException(sandcx, exn.address())) { - MOZ_ASSERT(!ok); - JS_ClearPendingException(sandcx); - if (returnStringOnly) { - // The caller asked for strings only, convert the - // exception into a string. - JSString *str = JS_ValueToString(sandcx, exn); - exn = str ? JS::StringValue(str) : JS::UndefinedValue(); - } - } - } - - // - // Alright, we're back on the caller's cx. If an error occured, try to - // wrap and set the exception. Otherwise, wrap the return value. - // - - if (!ok) { - // If we end up without an exception, it was probably due to OOM along - // the way, in which case we thow. Otherwise, wrap it. - if (exn.isUndefined() || !JS_WrapValue(cx, exn.address())) - return NS_ERROR_OUT_OF_MEMORY; - - // Set the exception on our caller's cx. - JS_SetPendingException(cx, exn); - return NS_ERROR_FAILURE; - } - - // Transitively apply Xray waivers if |sb| was waived. - if (waiveXray) { - ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, v.address()); - } else { - ok = JS_WrapValue(cx, v.address()); - } - NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE); - - // Whew! - rval.set(v); - return NS_OK; -} - /* JSObject import (in AUTF8String registryLocation, * [optional] in JSObject targetObj); */ @@ -4564,73 +3098,6 @@ nsXPCComponents_Utils::CreateDateIn(const Value &vobj, int64_t msec, JSContext * return NS_OK; } -bool -NonCloningFunctionForwarder(JSContext *cx, unsigned argc, Value *vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0)); - MOZ_ASSERT(v.isObject(), "weird function"); - - JSObject *obj = JS_THIS_OBJECT(cx, vp); - if (!obj) { - return false; - } - return JS_CallFunctionValue(cx, obj, v, args.length(), args.array(), vp); -} - -/* - * Forwards the call to the exported function. Clones all the non reflectors, ignores - * the |this| argument. - */ -bool -CloningFunctionForwarder(JSContext *cx, unsigned argc, Value *vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0)); - NS_ASSERTION(v.isObject(), "weird function"); - RootedObject origFunObj(cx, UncheckedUnwrap(&v.toObject())); - { - JSAutoCompartment ac(cx, origFunObj); - // Note: only the arguments are cloned not the |this| or the |callee|. - // Function forwarder does not use those. - for (unsigned i = 0; i < args.length(); i++) { - if (!CloneNonReflectors(cx, args[i])) { - return false; - } - } - - // JS API does not support any JSObject to JSFunction conversion, - // so let's use JS_CallFunctionValue instead. - RootedValue functionVal(cx); - functionVal.setObject(*origFunObj); - - if (!JS_CallFunctionValue(cx, nullptr, functionVal, args.length(), args.array(), vp)) - return false; - } - - // Return value must be wrapped. - return JS_WrapValue(cx, vp); -} - -bool -NewFunctionForwarder(JSContext *cx, HandleId id, HandleObject callable, bool doclone, - MutableHandleValue vp) -{ - JSFunction *fun = js::NewFunctionByIdWithReserved(cx, doclone ? CloningFunctionForwarder : - NonCloningFunctionForwarder, - 0,0, JS::CurrentGlobalOrNull(cx), id); - - if (!fun) - return false; - - JSObject *funobj = JS_GetFunctionObject(fun); - js::SetFunctionNativeReserved(funobj, 0, ObjectValue(*callable)); - vp.setObject(*funobj); - return true; -} - /* void makeObjectPropsNormal(jsval vobj); */ NS_IMETHODIMP nsXPCComponents_Utils::MakeObjectPropsNormal(const Value &vobj, JSContext *cx) @@ -4860,7 +3327,7 @@ nsXPCComponents_Utils::NukeSandbox(const JS::Value &obj, JSContext *cx) JSObject *wrapper = &obj.toObject(); NS_ENSURE_TRUE(IsWrapper(wrapper), NS_ERROR_INVALID_ARG); JSObject *sb = UncheckedUnwrap(wrapper); - NS_ENSURE_TRUE(GetObjectJSClass(sb) == &SandboxClass, NS_ERROR_INVALID_ARG); + NS_ENSURE_TRUE(IsSandbox(sb), NS_ERROR_INVALID_ARG); NukeCrossCompartmentWrappers(cx, AllCompartments(), SingleCompartment(GetObjectCompartment(sb)), NukeWindowReferences); diff --git a/js/xpconnect/src/moz.build b/js/xpconnect/src/moz.build index 5adf20a1bd77..da8880f24f37 100644 --- a/js/xpconnect/src/moz.build +++ b/js/xpconnect/src/moz.build @@ -51,6 +51,7 @@ CPP_SOURCES += [ 'nsCxPusher.cpp', 'nsScriptError.cpp', 'nsXPConnect.cpp', + 'Sandbox.cpp', ] FAIL_ON_WARNINGS = True diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 9042a47fb210..8fa989ab3fa9 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -3681,7 +3681,22 @@ xpc_GetSafeJSContext() return XPCJSRuntime::Get()->GetJSContextStack()->GetSafeJSContext(); } +bool +NewFunctionForwarder(JSContext *cx, JS::HandleId id, JS::HandleObject callable, + bool doclone, JS::MutableHandleValue vp); + +nsresult +ThrowAndFail(nsresult errNum, JSContext* cx, bool* retval); + +// Infallible. +already_AddRefed +NewSandboxConstructor(); + +bool +IsSandbox(JSObject *obj); + namespace xpc { + struct SandboxOptions { SandboxOptions(JSContext *cx) : wantXrays(true) From 033e608a9060599fb8cc4172325c2a1bc4b799c6 Mon Sep 17 00:00:00 2001 From: Makoto Kato Date: Thu, 22 Aug 2013 19:31:48 +0900 Subject: [PATCH 34/54] Bug 901820 - Part 1. replace nsCRT::strdup with strdup. r=bsmedberg --- intl/locale/src/os2/nsCollationOS2.cpp | 2 +- intl/strres/src/nsStringBundle.cpp | 2 +- intl/unicharutil/src/nsSaveAsCharset.cpp | 2 +- netwerk/base/src/nsStandardURL.cpp | 11 ++++++++--- netwerk/protocol/http/nsHttpAuthCache.cpp | 4 ++-- netwerk/protocol/http/nsHttpHandler.cpp | 6 +++--- netwerk/streamconv/converters/mozTXTToHTMLConv.cpp | 2 +- netwerk/test/TestRes.cpp | 8 ++++---- security/manager/ssl/src/nsCMSSecureMessage.cpp | 4 ++-- security/manager/ssl/src/nsKeygenHandler.cpp | 2 +- security/manager/ssl/src/nsPKCS12Blob.cpp | 2 +- toolkit/components/startup/nsUserInfoUnix.cpp | 6 +++--- widget/gtk2/nsDragService.cpp | 2 +- widget/windows/nsDeviceContextSpecWin.cpp | 12 ++++++------ widget/windows/nsPrintSettingsWin.cpp | 4 ++-- widget/xpwidgets/nsTransferable.cpp | 4 ++-- xpcom/base/nsTraceRefcntImpl.cpp | 5 ++--- xpcom/ds/nsProperties.cpp | 2 +- xpfe/components/directory/nsDirectoryViewer.cpp | 4 ++-- 19 files changed, 44 insertions(+), 40 deletions(-) diff --git a/intl/locale/src/os2/nsCollationOS2.cpp b/intl/locale/src/os2/nsCollationOS2.cpp index a1700b32583d..f020fa7a0fce 100644 --- a/intl/locale/src/os2/nsCollationOS2.cpp +++ b/intl/locale/src/os2/nsCollationOS2.cpp @@ -131,7 +131,7 @@ nsresult nsCollationOS2::AllocateRawSortKey(int32_t strength, if (uLen < iBufferLength) { // Success! // Give 'em the real size in bytes... - *key = (uint8_t *)nsCRT::strdup((PRUnichar*) pLocalBuffer); + *key = (uint8_t *)NS_strdup((PRUnichar*) pLocalBuffer); *outLen = uLen * 2 + 2; res = NS_OK; } diff --git a/intl/strres/src/nsStringBundle.cpp b/intl/strres/src/nsStringBundle.cpp index 2a27ee1e6b3c..c0fc1b54016e 100644 --- a/intl/strres/src/nsStringBundle.cpp +++ b/intl/strres/src/nsStringBundle.cpp @@ -771,7 +771,7 @@ nsStringBundleService::FormatStatusMessage(nsresult aStatus, // XXX hack for mailnews who has already formatted their messages: if (aStatus == NS_OK && aStatusArg) { - *result = nsCRT::strdup(aStatusArg); + *result = NS_strdup(aStatusArg); NS_ENSURE_TRUE(*result, NS_ERROR_OUT_OF_MEMORY); return NS_OK; } diff --git a/intl/unicharutil/src/nsSaveAsCharset.cpp b/intl/unicharutil/src/nsSaveAsCharset.cpp index fdae0edff8a7..282e3fbfa5bd 100644 --- a/intl/unicharutil/src/nsSaveAsCharset.cpp +++ b/intl/unicharutil/src/nsSaveAsCharset.cpp @@ -122,7 +122,7 @@ nsSaveAsCharset::GetCharset(char * *aCharset) return NS_ERROR_FAILURE; } - *aCharset = nsCRT::strdup(charset); + *aCharset = strdup(charset); return (*aCharset) ? NS_OK : NS_ERROR_OUT_OF_MEMORY; } diff --git a/netwerk/base/src/nsStandardURL.cpp b/netwerk/base/src/nsStandardURL.cpp index 8476ffad6a5a..ae872c8a9f4b 100644 --- a/netwerk/base/src/nsStandardURL.cpp +++ b/netwerk/base/src/nsStandardURL.cpp @@ -284,7 +284,9 @@ nsStandardURL::~nsStandardURL() { LOG(("Destroying nsStandardURL @%p\n", this)); - CRTFREEIF(mHostA); + if (mHostA) { + free(mHostA); + } #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN PR_REMOVE_LINK(&mDebugCList); #endif @@ -375,7 +377,10 @@ nsStandardURL::InvalidateCache(bool invalidateCachedFile) { if (invalidateCachedFile) mFile = 0; - CRTFREEIF(mHostA); + if (mHostA) { + free(mHostA); + mHostA = nullptr; + } mSpecEncoding = eEncoding_Unknown; } @@ -1743,7 +1748,7 @@ nsStandardURL::CloneInternal(nsStandardURL::RefHandlingEnum refHandlingMode, clone->mURLType = mURLType; clone->mParser = mParser; clone->mFile = mFile; - clone->mHostA = mHostA ? nsCRT::strdup(mHostA) : nullptr; + clone->mHostA = mHostA ? strdup(mHostA) : nullptr; clone->mMutable = true; clone->mSupportsFileURL = mSupportsFileURL; clone->mHostEncoding = mHostEncoding; diff --git a/netwerk/protocol/http/nsHttpAuthCache.cpp b/netwerk/protocol/http/nsHttpAuthCache.cpp index f2f29d28caa2..a60dfe717df0 100644 --- a/netwerk/protocol/http/nsHttpAuthCache.cpp +++ b/netwerk/protocol/http/nsHttpAuthCache.cpp @@ -169,7 +169,7 @@ nsHttpAuthCache::SetAuthEntry(const char *scheme, if (NS_FAILED(rv)) delete node; else - PL_HashTableAdd(mDB, nsCRT::strdup(key.get()), node); + PL_HashTableAdd(mDB, strdup(key.get()), node); return rv; } @@ -254,7 +254,7 @@ nsHttpAuthCache::FreeEntry(void *self, PLHashEntry *he, unsigned flag) else if (flag == HT_FREE_ENTRY) { // three wonderful flavors of freeing memory ;-) delete (nsHttpAuthNode *) he->value; - nsCRT::free((char *) he->key); + free((char *) he->key); free(he); } } diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp index 2ff39548fffc..544da9be70f9 100644 --- a/netwerk/protocol/http/nsHttpHandler.cpp +++ b/netwerk/protocol/http/nsHttpHandler.cpp @@ -1421,7 +1421,7 @@ PrepareAcceptLanguages(const char *i_AcceptLanguages, nsACString &o_AcceptLangua const char *comma; int32_t available; - o_Accept = nsCRT::strdup(i_AcceptLanguages); + o_Accept = strdup(i_AcceptLanguages); if (!o_Accept) return NS_ERROR_OUT_OF_MEMORY; for (p = o_Accept, n = size = 0; '\0' != *p; p++) { @@ -1432,7 +1432,7 @@ PrepareAcceptLanguages(const char *i_AcceptLanguages, nsACString &o_AcceptLangua available = size + ++n * 11 + 1; q_Accept = new char[available]; if (!q_Accept) { - nsCRT::free(o_Accept); + free(o_Accept); return NS_ERROR_OUT_OF_MEMORY; } *q_Accept = '\0'; @@ -1479,7 +1479,7 @@ PrepareAcceptLanguages(const char *i_AcceptLanguages, nsACString &o_AcceptLangua MOZ_ASSERT(available > 0, "allocated string not long enough"); } } - nsCRT::free(o_Accept); + free(o_Accept); o_AcceptLanguages.Assign((const char *) q_Accept); delete [] q_Accept; diff --git a/netwerk/streamconv/converters/mozTXTToHTMLConv.cpp b/netwerk/streamconv/converters/mozTXTToHTMLConv.cpp index 2184b0c931a0..ba82a38a2ae0 100644 --- a/netwerk/streamconv/converters/mozTXTToHTMLConv.cpp +++ b/netwerk/streamconv/converters/mozTXTToHTMLConv.cpp @@ -1347,7 +1347,7 @@ mozTXTToHTMLConv::ScanTXT(const PRUnichar *text, uint32_t whattodo, // need to reallocate and re-copy the characters already in the out String. NS_ASSERTION(inLength, "ScanTXT passed 0 length string"); if (inLength == 0) { - *_retval = nsCRT::strdup(text); + *_retval = NS_strdup(text); return NS_OK; } diff --git a/netwerk/test/TestRes.cpp b/netwerk/test/TestRes.cpp index 5a925c560939..74e7d00261b0 100644 --- a/netwerk/test/TestRes.cpp +++ b/netwerk/test/TestRes.cpp @@ -87,14 +87,14 @@ TestOpenInputStream(const char* url) rv = uri->GetSpec(&str); if (NS_FAILED(rv)) return rv; fprintf(stdout, "%s resolved to ", str); - nsCRT::free(str); + free(str); rv = channel->GetURI(getter_AddRefs(uri)); if (NS_FAILED(rv)) return rv; rv = uri->GetSpec(&str); if (NS_FAILED(rv)) return rv; fprintf(stdout, "%s\n", str); - nsCRT::free(str); + free(str); return NS_OK; } @@ -123,7 +123,7 @@ public: rv = uri->GetSpec(&str); if (NS_SUCCEEDED(rv)) { fprintf(stdout, "Starting to load %s\n", str); - nsCRT::free(str); + free(str); } } return NS_OK; @@ -141,7 +141,7 @@ public: rv = uri->GetSpec(&str); if (NS_SUCCEEDED(rv)) { fprintf(stdout, "Ending load %s, status=%x\n", str, aStatus); - nsCRT::free(str); + free(str); } } gDone = true; diff --git a/security/manager/ssl/src/nsCMSSecureMessage.cpp b/security/manager/ssl/src/nsCMSSecureMessage.cpp index 2183bb42e2b5..71830149aba1 100644 --- a/security/manager/ssl/src/nsCMSSecureMessage.cpp +++ b/security/manager/ssl/src/nsCMSSecureMessage.cpp @@ -122,7 +122,7 @@ DecodeCert(const char *value, nsIX509Cert ** _retval) rv = NS_ERROR_FAILURE; } - nsCRT::free((char*)data); + free((char*)data); return rv; } @@ -244,7 +244,7 @@ SendMessage(const char *msg, const char *base64Cert, char ** _retval) rv = encode(output.data, output.len, _retval); done: - if (certDER) nsCRT::free((char *)certDER); + if (certDER) free((char *)certDER); if (cert) CERT_DestroyCertificate(cert); if (cmsMsg) NSS_CMSMessage_Destroy(cmsMsg); if (arena) PORT_FreeArena(arena, false); /* false? */ diff --git a/security/manager/ssl/src/nsKeygenHandler.cpp b/security/manager/ssl/src/nsKeygenHandler.cpp index 7ee34761896a..63b5c33500aa 100644 --- a/security/manager/ssl/src/nsKeygenHandler.cpp +++ b/security/manager/ssl/src/nsKeygenHandler.cpp @@ -724,7 +724,7 @@ nsKeygenFormProcessor::GetPublicKey(nsAString& aValue, nsAString& aChallenge, } CopyASCIItoUTF16(keystring, aOutPublicKey); - nsCRT::free(keystring); + free(keystring); rv = NS_OK; loser: diff --git a/security/manager/ssl/src/nsPKCS12Blob.cpp b/security/manager/ssl/src/nsPKCS12Blob.cpp index 2e3afac54f70..bc154eaa1bb4 100644 --- a/security/manager/ssl/src/nsPKCS12Blob.cpp +++ b/security/manager/ssl/src/nsPKCS12Blob.cpp @@ -734,7 +734,7 @@ nsPKCS12Blob::nickname_collision(SECItem *oldNick, PRBool *cancel, void *wincx) return nullptr; newNick->type = siAsciiString; - newNick->data = (unsigned char*) nsCRT::strdup(nickname.get()); + newNick->data = (unsigned char*) strdup(nickname.get()); newNick->len = strlen((char*)newNick->data); return newNick; } diff --git a/toolkit/components/startup/nsUserInfoUnix.cpp b/toolkit/components/startup/nsUserInfoUnix.cpp index 8d1a9e97f65f..d2130d5f2f0b 100644 --- a/toolkit/components/startup/nsUserInfoUnix.cpp +++ b/toolkit/components/startup/nsUserInfoUnix.cpp @@ -92,7 +92,7 @@ nsUserInfo::GetUsername(char * *aUsername) printf("username = %s\n", pw->pw_name); #endif - *aUsername = nsCRT::strdup(pw->pw_name); + *aUsername = strdup(pw->pw_name); return NS_OK; } @@ -117,7 +117,7 @@ nsUserInfo::GetDomain(char * *aDomain) #endif if (domainname && domainname[0]) { - *aDomain = nsCRT::strdup(domainname); + *aDomain = strdup(domainname); rv = NS_OK; } else { @@ -128,7 +128,7 @@ nsUserInfo::GetDomain(char * *aDomain) // if the nodename is foo.bar.org, use bar.org as the domain char *pos = strchr(buf.nodename,'.'); if (pos) { - *aDomain = nsCRT::strdup(pos+1); + *aDomain = strdup(pos+1); rv = NS_OK; } } diff --git a/widget/gtk2/nsDragService.cpp b/widget/gtk2/nsDragService.cpp index 8926610737ae..b00b79e033ea 100644 --- a/widget/gtk2/nsDragService.cpp +++ b/widget/gtk2/nsDragService.cpp @@ -1459,7 +1459,7 @@ nsDragService::SourceDataGet(GtkWidget *aWidget, PR_LOG(sDragLm, PR_LOG_DEBUG, ("Type is %s\n", typeName)); // make a copy since |nsXPIDLCString| won't use |g_free|... - mimeFlavor.Adopt(nsCRT::strdup(typeName)); + mimeFlavor.Adopt(strdup(typeName)); g_free(typeName); // check to make sure that we have data items to return. if (!mSourceDataItems) { diff --git a/widget/windows/nsDeviceContextSpecWin.cpp b/widget/windows/nsDeviceContextSpecWin.cpp index 134d74faa41e..9f244e9068f6 100644 --- a/widget/windows/nsDeviceContextSpecWin.cpp +++ b/widget/windows/nsDeviceContextSpecWin.cpp @@ -401,21 +401,21 @@ NS_IMETHODIMP nsDeviceContextSpecWin::Init(nsIWidget* aWidget, if (!aIsPrintPreview) { rv = CheckForPrintToFile(mPrintSettings, deviceName, nullptr); if (NS_FAILED(rv)) { - nsCRT::free(deviceName); - nsCRT::free(driverName); + free(deviceName); + free(driverName); return NS_ERROR_FAILURE; } } // clean up - nsCRT::free(deviceName); - nsCRT::free(driverName); + free(deviceName); + free(driverName); return NS_OK; } else { PR_PL(("***** nsDeviceContextSpecWin::Init - deviceName/driverName/devMode was NULL!\n")); - if (deviceName) nsCRT::free(deviceName); - if (driverName) nsCRT::free(driverName); + if (deviceName) free(deviceName); + if (driverName) free(driverName); if (devMode) ::HeapFree(::GetProcessHeap(), 0, devMode); } } diff --git a/widget/windows/nsPrintSettingsWin.cpp b/widget/windows/nsPrintSettingsWin.cpp index 11ce3de9816c..2edcc32d97da 100644 --- a/widget/windows/nsPrintSettingsWin.cpp +++ b/widget/windows/nsPrintSettingsWin.cpp @@ -132,11 +132,11 @@ nsPrintSettingsWin& nsPrintSettingsWin::operator=(const nsPrintSettingsWin& rhs) ((nsPrintSettings&) *this) = rhs; if (mDeviceName) { - nsCRT::free(mDeviceName); + free(mDeviceName); } if (mDriverName) { - nsCRT::free(mDriverName); + free(mDriverName); } // Use free because we used the native malloc to create the memory diff --git a/widget/xpwidgets/nsTransferable.cpp b/widget/xpwidgets/nsTransferable.cpp index 996fd3646813..a3b89c5f0cdd 100644 --- a/widget/xpwidgets/nsTransferable.cpp +++ b/widget/xpwidgets/nsTransferable.cpp @@ -51,7 +51,7 @@ uint32_t GetDataForFlavor (const nsTArray& aArray, //------------------------------------------------------------------------- DataStruct::~DataStruct() { - if (mCacheFileName) nsCRT::free(mCacheFileName); + if (mCacheFileName) free(mCacheFileName); } //------------------------------------------------------------------------- @@ -133,7 +133,7 @@ DataStruct::WriteCache(nsISupports* aData, uint32_t aDataLen) if (!mCacheFileName) { nsXPIDLCString fName; cacheFile->GetNativeLeafName(fName); - mCacheFileName = nsCRT::strdup(fName); + mCacheFileName = strdup(fName); } // write out the contents of the clipboard diff --git a/xpcom/base/nsTraceRefcntImpl.cpp b/xpcom/base/nsTraceRefcntImpl.cpp index d16415e256c3..2391077cacd7 100644 --- a/xpcom/base/nsTraceRefcntImpl.cpp +++ b/xpcom/base/nsTraceRefcntImpl.cpp @@ -185,8 +185,7 @@ static void TypesToLogFreeEntry(void *pool, PLHashEntry *he, unsigned flag) { if (flag == HT_FREE_ENTRY) { - nsCRT::free(const_cast - (reinterpret_cast(he->key))); + free(const_cast(reinterpret_cast(he->key))); PR_Free(he); } } @@ -758,7 +757,7 @@ static void InitTraceLog(void) if (cm) { *cm = '\0'; } - PL_HashTableAdd(gTypesToLog, nsCRT::strdup(cp), (void*)1); + PL_HashTableAdd(gTypesToLog, strdup(cp), (void*)1); fprintf(stdout, "%s ", cp); if (!cm) break; *cm = ','; diff --git a/xpcom/ds/nsProperties.cpp b/xpcom/ds/nsProperties.cpp index bb17808c90ad..0ab910cd899e 100644 --- a/xpcom/ds/nsProperties.cpp +++ b/xpcom/ds/nsProperties.cpp @@ -70,7 +70,7 @@ GetKeysEnumerate(const char *key, nsISupports* data, void *arg) { GetKeysEnumData *gkedp = (GetKeysEnumData *)arg; - gkedp->keys[gkedp->next] = nsCRT::strdup(key); + gkedp->keys[gkedp->next] = strdup(key); if (!gkedp->keys[gkedp->next]) { gkedp->res = NS_ERROR_OUT_OF_MEMORY; diff --git a/xpfe/components/directory/nsDirectoryViewer.cpp b/xpfe/components/directory/nsDirectoryViewer.cpp index aeb2f929a856..e0d8f991abfc 100644 --- a/xpfe/components/directory/nsDirectoryViewer.cpp +++ b/xpfe/components/directory/nsDirectoryViewer.cpp @@ -705,7 +705,7 @@ void nsHTTPIndex::GetDestination(nsIRDFResource* r, nsXPIDLCString& dest) { if (!url) { const char* temp; r->GetValueConst(&temp); - dest.Adopt(temp ? nsCRT::strdup(temp) : 0); + dest.Adopt(temp ? strdup(temp) : 0); } else { const PRUnichar* uri; url->GetValueConst(&uri); @@ -756,7 +756,7 @@ nsHTTPIndex::GetURI(char * *uri) if (! uri) return(NS_ERROR_NULL_POINTER); - if ((*uri = nsCRT::strdup("rdf:httpindex")) == nullptr) + if ((*uri = strdup("rdf:httpindex")) == nullptr) return(NS_ERROR_OUT_OF_MEMORY); return(NS_OK); From 584c783c772613c848bfa75d70e4885f429b6665 Mon Sep 17 00:00:00 2001 From: Makoto Kato Date: Thu, 22 Aug 2013 19:32:11 +0900 Subject: [PATCH 35/54] Bug 901820 - Part 2. Remove nsCRT::strdup/nsCRT::strndup impl. r=bsmedberg --- intl/uconv/ucvcn/nsGBKConvUtil.cpp | 1 - xpcom/ds/moz.build | 1 - xpcom/ds/nsCRT.cpp | 18 ----- xpcom/ds/nsCRT.h | 26 -------- xpcom/ds/nsCppSharedAllocator.h | 104 ----------------------------- xpcom/glue/nsDeque.cpp | 2 +- xpcom/io/nsPipe3.cpp | 1 + xpcom/io/nsSegmentedBuffer.cpp | 2 +- 8 files changed, 3 insertions(+), 152 deletions(-) delete mode 100644 xpcom/ds/nsCppSharedAllocator.h diff --git a/intl/uconv/ucvcn/nsGBKConvUtil.cpp b/intl/uconv/ucvcn/nsGBKConvUtil.cpp index 033b615545e9..e343306538a9 100644 --- a/intl/uconv/ucvcn/nsGBKConvUtil.cpp +++ b/intl/uconv/ucvcn/nsGBKConvUtil.cpp @@ -5,7 +5,6 @@ #include "nsGBKConvUtil.h" #include "gbku.h" -#include "nsCRT.h" #include "nsDebug.h" #define MAX_GBK_LENGTH 24066 /* (0xfe-0x80)*(0xfe-0x3f) */ //-------------------------------------------------------------------- diff --git a/xpcom/ds/moz.build b/xpcom/ds/moz.build index 01a7d07ca520..4f29be692639 100644 --- a/xpcom/ds/moz.build +++ b/xpcom/ds/moz.build @@ -51,7 +51,6 @@ EXPORTS += [ 'nsCRT.h', 'nsCharSeparatedTokenizer.h', 'nsCheapSets.h', - 'nsCppSharedAllocator.h', 'nsExpirationTracker.h', 'nsHashPropertyBag.h', 'nsHashtable.h', diff --git a/xpcom/ds/nsCRT.cpp b/xpcom/ds/nsCRT.cpp index 0e8122bbf829..07557fdeb733 100644 --- a/xpcom/ds/nsCRT.cpp +++ b/xpcom/ds/nsCRT.cpp @@ -149,24 +149,6 @@ const char* nsCRT::memmem(const char* haystack, uint32_t haystackLen, return NULL; } -PRUnichar* nsCRT::strdup(const PRUnichar* str) -{ - uint32_t len = NS_strlen(str); - return strndup(str, len); -} - -PRUnichar* nsCRT::strndup(const PRUnichar* str, uint32_t len) -{ - nsCppSharedAllocator shared_allocator; - PRUnichar* rslt = shared_allocator.allocate(len + 1); // add one for the null - // PRUnichar* rslt = new PRUnichar[len + 1]; - - if (rslt == NULL) return NULL; - memcpy(rslt, str, len * sizeof(PRUnichar)); - rslt[len] = 0; - return rslt; -} - // This should use NSPR but NSPR isn't exporting its PR_strtoll function // Until then... int64_t nsCRT::atoll(const char *str) diff --git a/xpcom/ds/nsCRT.h b/xpcom/ds/nsCRT.h index be121a21e31d..4f8bea17accc 100644 --- a/xpcom/ds/nsCRT.h +++ b/xpcom/ds/nsCRT.h @@ -10,7 +10,6 @@ #include #include "plstr.h" #include "nscore.h" -#include "nsCppSharedAllocator.h" #include "nsCRTGlue.h" #if defined(XP_WIN) || defined(XP_OS2) @@ -102,18 +101,6 @@ public: return int32_t(PL_strncmp(s1,s2,unsigned(aMaxLen))); } - static char* strdup(const char* str) { - return PL_strdup(str); - } - - static char* strndup(const char* str, uint32_t len) { - return PL_strndup(str, len); - } - - static void free(char* str) { - PL_strfree(str); - } - /** How to use this fancy (thread-safe) version of strtok: @@ -147,19 +134,6 @@ public: static const char* memmem(const char* haystack, uint32_t haystackLen, const char* needle, uint32_t needleLen); - // You must use nsCRT::free(PRUnichar*) to free memory allocated - // by nsCRT::strdup(PRUnichar*). - static PRUnichar* strdup(const PRUnichar* str); - - // You must use nsCRT::free(PRUnichar*) to free memory allocated - // by strndup(PRUnichar*, uint32_t). - static PRUnichar* strndup(const PRUnichar* str, uint32_t len); - - static void free(PRUnichar* str) { - nsCppSharedAllocator shared_allocator; - shared_allocator.deallocate(str, 0 /*we never new or kept the size*/); - } - // String to longlong static int64_t atoll(const char *str); diff --git a/xpcom/ds/nsCppSharedAllocator.h b/xpcom/ds/nsCppSharedAllocator.h deleted file mode 100644 index 275d724d7665..000000000000 --- a/xpcom/ds/nsCppSharedAllocator.h +++ /dev/null @@ -1,104 +0,0 @@ -/* 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 nsCppSharedAllocator_h__ -#define nsCppSharedAllocator_h__ - -#include "nsMemory.h" // for |nsMemory| -#include // to allow placement |new| - - - // under MSVC shut off copious warnings about unused in-lines -#ifdef _MSC_VER - #pragma warning( disable: 4514 ) -#endif - -#include - - -template -class nsCppSharedAllocator - /* - ...allows Standard Library containers, et al, to use our global shared - (XP)COM-aware allocator. - */ - { - public: - typedef T value_type; - typedef size_t size_type; - typedef ptrdiff_t difference_type; - - typedef T* pointer; - typedef const T* const_pointer; - - typedef T& reference; - typedef const T& const_reference; - - - - nsCppSharedAllocator() { } - - ~nsCppSharedAllocator() { } - - - pointer - address( reference r ) const - { - return &r; - } - - const_pointer - address( const_reference r ) const - { - return &r; - } - - pointer - allocate( size_type n, const void* /*hint*/=0 ) - { - return reinterpret_cast(nsMemory::Alloc(static_cast(n*sizeof(T)))); - } - - void - deallocate( pointer p, size_type /*n*/ ) - { - nsMemory::Free(p); - } - - void - construct( pointer p, const T& val ) - { - new (p) T(val); - } - - void - destroy( pointer p ) - { - p->~T(); - } - - size_type - max_size() const - { - return ULONG_MAX / sizeof(T); - } - - }; - - -template -bool -operator==( const nsCppSharedAllocator&, const nsCppSharedAllocator& ) - { - return true; - } - -template -bool -operator!=( const nsCppSharedAllocator&, const nsCppSharedAllocator& ) - { - return false; - } - -#endif /* !defined(nsCppSharedAllocator_h__) */ diff --git a/xpcom/glue/nsDeque.cpp b/xpcom/glue/nsDeque.cpp index 853b1fdab5b0..8f565bb563df 100644 --- a/xpcom/glue/nsDeque.cpp +++ b/xpcom/glue/nsDeque.cpp @@ -4,8 +4,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsDeque.h" -#include "nsCRT.h" #include "nsTraceRefcnt.h" +#include #ifdef DEBUG_rickg #include #endif diff --git a/xpcom/io/nsPipe3.cpp b/xpcom/io/nsPipe3.cpp index eaec4e82d7aa..2f4b17d1e5eb 100644 --- a/xpcom/io/nsPipe3.cpp +++ b/xpcom/io/nsPipe3.cpp @@ -15,6 +15,7 @@ #include "prlog.h" #include "nsIClassInfoImpl.h" #include "nsAlgorithm.h" +#include "nsMemory.h" using namespace mozilla; diff --git a/xpcom/io/nsSegmentedBuffer.cpp b/xpcom/io/nsSegmentedBuffer.cpp index 1938fa16f834..9a5d6c972577 100644 --- a/xpcom/io/nsSegmentedBuffer.cpp +++ b/xpcom/io/nsSegmentedBuffer.cpp @@ -4,7 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsSegmentedBuffer.h" -#include "nsCRT.h" +#include "nsMemory.h" nsresult nsSegmentedBuffer::Init(uint32_t segmentSize, uint32_t maxSize, From 2a55d19fdf091fd3a3f0d224fd6b4e94649a7bae Mon Sep 17 00:00:00 2001 From: Patrick McManus Date: Thu, 22 Aug 2013 08:52:50 -0400 Subject: [PATCH 36/54] bug 750932 - multipart delimiter check r=jduell --- netwerk/streamconv/converters/nsMultiMixedConv.cpp | 3 ++- netwerk/test/unit/xpcshell.ini | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/netwerk/streamconv/converters/nsMultiMixedConv.cpp b/netwerk/streamconv/converters/nsMultiMixedConv.cpp index 024c057ce04f..2a44ab0f2b07 100644 --- a/netwerk/streamconv/converters/nsMultiMixedConv.cpp +++ b/netwerk/streamconv/converters/nsMultiMixedConv.cpp @@ -565,7 +565,8 @@ nsMultiMixedConv::OnDataAvailable(nsIRequest *request, nsISupports *context, int32_t tokenLinefeed = 1; while ( (token = FindToken(cursor, bufLen)) ) { - if (*(token+mTokenLen+1) == '-') { + if (((token + mTokenLen) < (cursor + bufLen)) && + (*(token + mTokenLen + 1) == '-')) { // This was the last delimiter so we can stop processing rv = SendData(cursor, LengthToToken(cursor, token)); if (NS_FAILED(rv)) return rv; diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini index a577e0833318..171c09a5737e 100644 --- a/netwerk/test/unit/xpcshell.ini +++ b/netwerk/test/unit/xpcshell.ini @@ -129,8 +129,6 @@ skip-if = bits != 32 [test_multipart_byteranges.js] [test_multipart_streamconv.js] [test_multipart_streamconv_missing_lead_boundary.js] -# Bug 750932: test fails under AddressSanitizer -fail-if = asan [test_nestedabout_serialize.js] [test_net_addr.js] # Bug 732363: test fails on windows for unknown reasons. From 6495e92bc1d1c32edb005e25b1a6befcc6fa10e5 Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Thu, 22 Aug 2013 07:13:18 -0600 Subject: [PATCH 37/54] Bug 906060 - Allow ExclusiveContext zones to have TI enabled, r=billm. --- js/src/frontend/BytecodeEmitter.cpp | 3 +- js/src/jit/IonBuilder.cpp | 8 +- js/src/jit/MIR.cpp | 2 +- js/src/jsarray.cpp | 2 +- js/src/jscntxt.h | 5 +- js/src/jscntxtinlines.h | 12 +- js/src/jscompartment.h | 4 - js/src/jsgc.cpp | 14 +- js/src/jsgc.h | 2 +- js/src/jsinfer.cpp | 251 ++++++++++++++-------------- js/src/jsinfer.h | 42 ++--- js/src/jsinferinlines.h | 86 +++++----- js/src/jsobj.h | 2 +- js/src/jsworkers.cpp | 7 +- js/src/vm/Runtime.cpp | 4 +- js/src/vm/Shape.cpp | 2 +- 16 files changed, 229 insertions(+), 217 deletions(-) diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 8bbc20e1d9f1..b5e029724d84 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -4497,8 +4497,7 @@ EmitFunc(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn) */ if (fun->isInterpreted()) { bool singleton = - cx->isJSContext() && - cx->asJSContext()->typeInferenceEnabled() && + cx->typeInferenceEnabled() && bce->script->compileAndGo && fun->isInterpreted() && (bce->checkSingletonContext() || diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 0745e5b6cc6c..40067ff6731b 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -820,7 +820,7 @@ IonBuilder::initParameters() types::StackTypeSet *thisTypes = types::TypeScript::ThisTypes(script()); if (thisTypes->empty() && baselineFrame_) - thisTypes->addType(cx, types::GetValueType(cx, baselineFrame_->thisValue())); + thisTypes->addType(cx, types::GetValueType(baselineFrame_->thisValue())); MParameter *param = MParameter::New(MParameter::THIS_SLOT, cloneTypeSet(thisTypes)); current->add(param); @@ -831,7 +831,7 @@ IonBuilder::initParameters() if (argTypes->empty() && baselineFrame_ && !script_->baselineScript()->modifiesArguments()) { - argTypes->addType(cx, types::GetValueType(cx, baselineFrame_->argv()[i])); + argTypes->addType(cx, types::GetValueType(baselineFrame_->argv()[i])); } param = MParameter::New(i, cloneTypeSet(argTypes)); @@ -5773,7 +5773,7 @@ IonBuilder::newPendingLoopHeader(MBasicBlock *predecessor, jsbytecode *pc, bool MIRType type = existingValue.isDouble() ? MIRType_Double : MIRTypeFromValueType(existingValue.extractNonDoubleType()); - types::Type ntype = types::GetValueType(cx, existingValue); + types::Type ntype = types::GetValueType(existingValue); types::StackTypeSet *typeSet = GetIonContext()->temp->lifoAlloc()->new_(ntype); phi->addBackedgeType(type, typeSet); @@ -6363,7 +6363,7 @@ IonBuilder::jsop_intrinsic(HandlePropertyName name) if (!cx->global()->getIntrinsicValue(cx, name, &vp)) return false; - JS_ASSERT(types->hasType(types::GetValueType(cx, vp))); + JS_ASSERT(types->hasType(types::GetValueType(vp))); MConstant *ins = MConstant::New(vp); current->add(ins); diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 42a1c9d20589..cdc0231ca7a9 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -2546,7 +2546,7 @@ ion::PropertyReadNeedsTypeBarrier(JSContext *cx, types::TypeObject *object, Prop if (HasDataProperty(cx, obj, id, &v)) { if (v.isUndefined()) break; - observed->addType(cx, types::GetValueType(cx, v)); + observed->addType(cx, types::GetValueType(v)); } obj = obj->getProto(); diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 02e650a38e11..b2a2e1aa43cf 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -1075,7 +1075,7 @@ InitArrayTypes(JSContext *cx, TypeObject *type, const Value *vector, unsigned co for (unsigned i = 0; i < count; i++) { if (vector[i].isMagic(JS_ELEMENTS_HOLE)) continue; - Type valtype = GetValueType(cx, vector[i]); + Type valtype = GetValueType(vector[i]); types->addType(cx, valtype); } } diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 40a1fad3927b..2c7bd202cdde 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -259,10 +259,11 @@ struct ThreadSafeContext : ContextFriendFields, js_ReportAllocationOverflow(this); } - // Builtin atoms are immutable and may be accessed freely from any thread. + // Accessors for immutable runtime data. JSAtomState &names() { return runtime_->atomState; } StaticStrings &staticStrings() { return runtime_->staticStrings; } PropertyName *emptyString() { return runtime_->emptyString; } + FreeOp *defaultFreeOp() { return runtime_->defaultFreeOp(); } // GCs cannot happen while non-main threads are running. uint64_t gcNumber() { return runtime_->gcNumber; } @@ -351,6 +352,7 @@ class ExclusiveContext : public ThreadSafeContext // Zone local methods that can be used freely from an ExclusiveContext. inline bool typeInferenceEnabled() const; types::TypeObject *getNewType(Class *clasp, TaggedProto proto, JSFunction *fun = NULL); + inline js::LifoAlloc &typeLifoAlloc(); // Current global. This is only safe to use within the scope of the // AutoCompartment from which it's called. @@ -483,7 +485,6 @@ struct JSContext : public js::ExclusiveContext, js::LifoAlloc &tempLifoAlloc() { return runtime()->tempLifoAlloc; } inline js::LifoAlloc &analysisLifoAlloc(); - inline js::LifoAlloc &typeLifoAlloc(); #ifdef JS_THREADSAFE unsigned outstandingRequests;/* number of JS_BeginRequest calls diff --git a/js/src/jscntxtinlines.h b/js/src/jscntxtinlines.h index eb5030185a0f..ec1c308f46b6 100644 --- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -406,6 +406,12 @@ class AutoLockForExclusiveAccess MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; +inline LifoAlloc & +ExclusiveContext::typeLifoAlloc() +{ + return zone()->types.typeLifoAlloc; +} + } /* namespace js */ inline js::LifoAlloc & @@ -414,12 +420,6 @@ JSContext::analysisLifoAlloc() return compartment()->analysisLifoAlloc; } -inline js::LifoAlloc & -JSContext::typeLifoAlloc() -{ - return zone()->types.typeLifoAlloc; -} - inline void JSContext::setPendingException(js::Value v) { JS_ASSERT(!IsPoisonedValue(v)); diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index d01ee66ac6c4..21bea397d51a 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -433,10 +433,6 @@ namespace js { inline bool ExclusiveContext::typeInferenceEnabled() const { - // Type inference cannot be enabled in compartments which are accessed off - // the main thread by an ExclusiveContext. TI data is stored in per-zone - // allocators which could otherwise race with main thread operations. - JS_ASSERT_IF(!isJSContext(), !compartment_->zone()->types.inferenceEnabled); return compartment_->zone()->types.inferenceEnabled; } diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 26dd00fe2a7b..c03c06db7965 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -4055,6 +4055,13 @@ AutoGCSession::AutoGCSession(JSRuntime *rt) runtime->gcInterFrameGC = true; runtime->gcNumber++; + +#ifdef DEBUG + // Threads with an exclusive context should never pause while they are in + // the middle of a suppressGC. + for (ThreadDataIter iter(rt); !iter.done(); iter.next()) + JS_ASSERT(!iter->suppressGC); +#endif } AutoGCSession::~AutoGCSession() @@ -4816,6 +4823,9 @@ gc::MergeCompartments(JSCompartment *source, JSCompartment *target) target->zone()->allocator.arenas.adoptArenas(rt, &source->zone()->allocator.arenas); target->zone()->gcBytes += source->zone()->gcBytes; source->zone()->gcBytes = 0; + + // Merge other info in source's zone into target's zone. + target->zone()->types.typeLifoAlloc.transferFrom(&source->zone()->types.typeLifoAlloc); } void @@ -5140,8 +5150,8 @@ AutoMaybeTouchDeadZones::~AutoMaybeTouchDeadZones() runtime->gcManipulatingDeadZones = manipulatingDeadZones; } -AutoSuppressGC::AutoSuppressGC(JSContext *cx) - : suppressGC_(cx->runtime()->mainThread.suppressGC) +AutoSuppressGC::AutoSuppressGC(ExclusiveContext *cx) + : suppressGC_(cx->perThreadData->suppressGC) { suppressGC_++; } diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 220d7bc92ceb..e38b603d12e7 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -1400,7 +1400,7 @@ class AutoSuppressGC int32_t &suppressGC_; public: - AutoSuppressGC(JSContext *cx); + AutoSuppressGC(ExclusiveContext *cx); AutoSuppressGC(JSCompartment *comp); ~AutoSuppressGC() diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 3e2bda689d98..3c99f27170a3 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -47,6 +47,7 @@ using namespace js::types; using namespace js::analyze; using mozilla::DebugOnly; +using mozilla::Maybe; using mozilla::PodArrayZero; using mozilla::PodCopy; using mozilla::PodZero; @@ -259,7 +260,7 @@ types::TypeHasProperty(JSContext *cx, TypeObject *obj, jsid id, const Value &val if (cx->compartment()->types.pendingCount) return true; - Type type = GetValueType(cx, value); + Type type = GetValueType(value); AutoEnterAnalysis enter(cx); @@ -268,7 +269,7 @@ types::TypeHasProperty(JSContext *cx, TypeObject *obj, jsid id, const Value &val * haven't yet been accessed during analysis of the inheriting object. * Don't do the property instantiation now. */ - TypeSet *types = obj->maybeGetProperty(id, cx); + TypeSet *types = obj->maybeGetProperty(cx, id); if (!types) return true; @@ -1127,7 +1128,7 @@ GetSingletonPropertyType(JSContext *cx, JSObject *rawObjArg, HandleId id) if (HasDataProperty(cx, obj, id, v.address())) { if (v.isUndefined()) return Type::UnknownType(); - return GetValueType(cx, v); + return GetValueType(v); } obj = obj->getProto(); @@ -1838,23 +1839,27 @@ HeapTypeSet::HasObjectFlags(JSContext *cx, TypeObject *object, TypeObjectFlags f } static inline void -ObjectStateChange(JSContext *cx, TypeObject *object, bool markingUnknown, bool force) +ObjectStateChange(ExclusiveContext *cxArg, TypeObject *object, bool markingUnknown, bool force) { if (object->unknownProperties()) return; /* All constraints listening to state changes are on the empty id. */ - TypeSet *types = object->maybeGetProperty(JSID_EMPTY, cx); + TypeSet *types = object->maybeGetProperty(cxArg, JSID_EMPTY); /* Mark as unknown after getting the types, to avoid assertion. */ if (markingUnknown) object->flags |= OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES; if (types) { - TypeConstraint *constraint = types->constraintList; - while (constraint) { - constraint->newObjectState(cx, object, force); - constraint = constraint->next; + if (JSContext *cx = cxArg->maybeJSContext()) { + TypeConstraint *constraint = types->constraintList; + while (constraint) { + constraint->newObjectState(cx, object, force); + constraint = constraint->next; + } + } else { + JS_ASSERT(!types->constraintList); } } } @@ -2192,7 +2197,7 @@ StackTypeSet::propertyNeedsBarrier(JSContext *cx, jsid id) if (otype->unknownProperties()) return true; - if (types::HeapTypeSet *propTypes = otype->maybeGetProperty(typeId, cx)) { + if (types::HeapTypeSet *propTypes = otype->maybeGetProperty(cx, typeId)) { if (propTypes->needsBarrier(cx)) return true; } @@ -2612,7 +2617,7 @@ TypeCompartment::processPendingRecompiles(FreeOp *fop) } void -TypeCompartment::setPendingNukeTypes(JSContext *cx) +TypeCompartment::setPendingNukeTypes(ExclusiveContext *cx) { TypeZone *zone = &compartment()->zone()->types; if (!zone->pendingNukeTypes) { @@ -3030,9 +3035,9 @@ NumberTypes(Type a, Type b) * arrays and objects whose type can be fixed. */ static inline Type -GetValueTypeForTable(JSContext *cx, const Value &v) +GetValueTypeForTable(const Value &v) { - Type type = GetValueType(cx, v); + Type type = GetValueType(v); JS_ASSERT(!type.isSingleObject()); return type; } @@ -3058,7 +3063,8 @@ struct types::ArrayTableKey }; void -TypeCompartment::setTypeToHomogenousArray(JSContext *cx, JSObject *obj, Type elementType) +TypeCompartment::setTypeToHomogenousArray(ExclusiveContext *cx, + JSObject *obj, Type elementType) { if (!arrayTypeTable) { arrayTypeTable = cx->new_(); @@ -3097,7 +3103,7 @@ TypeCompartment::setTypeToHomogenousArray(JSContext *cx, JSObject *obj, Type ele } void -TypeCompartment::fixArrayType(JSContext *cx, JSObject *obj) +TypeCompartment::fixArrayType(ExclusiveContext *cx, JSObject *obj) { AutoEnterAnalysis enter(cx); @@ -3113,10 +3119,10 @@ TypeCompartment::fixArrayType(JSContext *cx, JSObject *obj) if (len == 0) return; - Type type = GetValueTypeForTable(cx, obj->getDenseElement(0)); + Type type = GetValueTypeForTable(obj->getDenseElement(0)); for (unsigned i = 1; i < len; i++) { - Type ntype = GetValueTypeForTable(cx, obj->getDenseElement(i)); + Type ntype = GetValueTypeForTable(obj->getDenseElement(i)); if (ntype != type) { if (NumberTypes(type, ntype)) type = Type::DoubleType(); @@ -3129,17 +3135,14 @@ TypeCompartment::fixArrayType(JSContext *cx, JSObject *obj) } void -types::FixRestArgumentsType(ExclusiveContext *cxArg, JSObject *obj) +types::FixRestArgumentsType(ExclusiveContext *cx, JSObject *obj) { - if (cxArg->isJSContext()) { - JSContext *cx = cxArg->asJSContext(); - if (cx->typeInferenceEnabled()) - cx->compartment()->types.fixRestArgumentsType(cx, obj); - } + if (cx->typeInferenceEnabled()) + cx->compartment()->types.fixRestArgumentsType(cx, obj); } void -TypeCompartment::fixRestArgumentsType(JSContext *cx, JSObject *obj) +TypeCompartment::fixRestArgumentsType(ExclusiveContext *cx, JSObject *obj) { AutoEnterAnalysis enter(cx); @@ -3199,14 +3202,14 @@ struct types::ObjectTableEntry }; static inline void -UpdateObjectTableEntryTypes(JSContext *cx, ObjectTableEntry &entry, +UpdateObjectTableEntryTypes(ExclusiveContext *cx, ObjectTableEntry &entry, IdValuePair *properties, size_t nproperties) { if (entry.object->unknownProperties()) return; for (size_t i = 0; i < nproperties; i++) { Type type = entry.types[i]; - Type ntype = GetValueTypeForTable(cx, properties[i].value); + Type ntype = GetValueTypeForTable(properties[i].value); if (ntype == type) continue; if (ntype.isPrimitive(JSVAL_TYPE_INT32) && @@ -3226,7 +3229,7 @@ UpdateObjectTableEntryTypes(JSContext *cx, ObjectTableEntry &entry, } void -TypeCompartment::fixObjectType(JSContext *cx, JSObject *obj) +TypeCompartment::fixObjectType(ExclusiveContext *cx, JSObject *obj) { AutoEnterAnalysis enter(cx); @@ -3299,7 +3302,7 @@ TypeCompartment::fixObjectType(JSContext *cx, JSObject *obj) for (size_t i = 0; i < properties.length(); i++) { ids[i] = properties[i].id; - types[i] = GetValueTypeForTable(cx, obj->getSlot(i)); + types[i] = GetValueTypeForTable(obj->getSlot(i)); if (!objType->unknownProperties()) objType->addPropertyType(cx, IdToTypeId(ids[i]), types[i]); } @@ -3427,7 +3430,7 @@ TypeObject::getFromPrototypes(JSContext *cx, jsid id, TypeSet *types, bool force } static inline void -UpdatePropertyType(JSContext *cx, TypeSet *types, JSObject *obj, Shape *shape, +UpdatePropertyType(ExclusiveContext *cx, TypeSet *types, JSObject *obj, Shape *shape, bool force) { types->setOwnProperty(cx, false); @@ -3445,14 +3448,14 @@ UpdatePropertyType(JSContext *cx, TypeSet *types, JSObject *obj, Shape *shape, * not collated into the JSID_VOID property (see propertySet comment). */ if (force || !value.isUndefined()) { - Type type = GetValueType(cx, value); + Type type = GetValueType(value); types->addType(cx, type); } } } bool -TypeObject::addProperty(JSContext *cx, jsid id, Property **pprop) +TypeObject::addProperty(ExclusiveContext *cx, jsid id, Property **pprop) { JS_ASSERT(!*pprop); Property *base = cx->typeLifoAlloc().new_(id); @@ -3483,7 +3486,7 @@ TypeObject::addProperty(JSContext *cx, jsid id, Property **pprop) for (size_t i = 0; i < singleton->getDenseInitializedLength(); i++) { const Value &value = singleton->getDenseElement(i); if (!value.isMagic(JS_ELEMENTS_HOLE)) { - Type type = GetValueType(cx, value); + Type type = GetValueType(value); base->types.setOwnProperty(cx, false); base->types.addType(cx, type); } @@ -3514,7 +3517,7 @@ TypeObject::addProperty(JSContext *cx, jsid id, Property **pprop) } bool -TypeObject::addDefiniteProperties(JSContext *cx, JSObject *obj) +TypeObject::addDefiniteProperties(ExclusiveContext *cx, JSObject *obj) { if (unknownProperties()) return true; @@ -3567,7 +3570,7 @@ TypeObject::matchDefiniteProperties(HandleObject obj) } inline void -InlineAddTypeProperty(JSContext *cx, TypeObject *obj, jsid id, Type type) +InlineAddTypeProperty(ExclusiveContext *cx, TypeObject *obj, jsid id, Type type) { JS_ASSERT(id == IdToTypeId(id)); @@ -3583,19 +3586,19 @@ InlineAddTypeProperty(JSContext *cx, TypeObject *obj, jsid id, Type type) } void -TypeObject::addPropertyType(JSContext *cx, jsid id, Type type) +TypeObject::addPropertyType(ExclusiveContext *cx, jsid id, Type type) { InlineAddTypeProperty(cx, this, id, type); } void -TypeObject::addPropertyType(JSContext *cx, jsid id, const Value &value) +TypeObject::addPropertyType(ExclusiveContext *cx, jsid id, const Value &value) { - InlineAddTypeProperty(cx, this, id, GetValueType(cx, value)); + InlineAddTypeProperty(cx, this, id, GetValueType(value)); } void -TypeObject::addPropertyType(JSContext *cx, const char *name, Type type) +TypeObject::addPropertyType(ExclusiveContext *cx, const char *name, Type type) { jsid id = JSID_VOID; if (name) { @@ -3611,13 +3614,13 @@ TypeObject::addPropertyType(JSContext *cx, const char *name, Type type) } void -TypeObject::addPropertyType(JSContext *cx, const char *name, const Value &value) +TypeObject::addPropertyType(ExclusiveContext *cx, const char *name, const Value &value) { - addPropertyType(cx, name, GetValueType(cx, value)); + addPropertyType(cx, name, GetValueType(value)); } void -TypeObject::markPropertyConfigured(JSContext *cx, jsid id) +TypeObject::markPropertyConfigured(ExclusiveContext *cx, jsid id) { AutoEnterAnalysis enter(cx); @@ -3629,24 +3632,28 @@ TypeObject::markPropertyConfigured(JSContext *cx, jsid id) } void -TypeObject::markStateChange(JSContext *cx) +TypeObject::markStateChange(ExclusiveContext *cxArg) { if (unknownProperties()) return; - AutoEnterAnalysis enter(cx); - TypeSet *types = maybeGetProperty(JSID_EMPTY, cx); + AutoEnterAnalysis enter(cxArg); + TypeSet *types = maybeGetProperty(cxArg, JSID_EMPTY); if (types) { - TypeConstraint *constraint = types->constraintList; - while (constraint) { - constraint->newObjectState(cx, this, true); - constraint = constraint->next; + if (JSContext *cx = cxArg->maybeJSContext()) { + TypeConstraint *constraint = types->constraintList; + while (constraint) { + constraint->newObjectState(cx, this, true); + constraint = constraint->next; + } + } else { + JS_ASSERT(!types->constraintList); } } } void -TypeObject::setFlags(JSContext *cx, TypeObjectFlags flags) +TypeObject::setFlags(ExclusiveContext *cx, TypeObjectFlags flags) { if ((this->flags & flags) == flags) return; @@ -3667,7 +3674,7 @@ TypeObject::setFlags(JSContext *cx, TypeObjectFlags flags) } void -TypeObject::markUnknown(JSContext *cx) +TypeObject::markUnknown(ExclusiveContext *cx) { AutoEnterAnalysis enter(cx); @@ -3701,7 +3708,7 @@ TypeObject::markUnknown(JSContext *cx) } void -TypeObject::clearNewScript(JSContext *cx) +TypeObject::clearNewScript(ExclusiveContext *cx) { JS_ASSERT(!(flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED)); flags |= OBJECT_FLAG_NEW_SCRIPT_CLEARED; @@ -3744,69 +3751,74 @@ TypeObject::clearNewScript(JSContext *cx) * script keeps track of where each property is initialized so we can walk * the stack and fix up any such objects. */ - Vector pcOffsets(cx); - for (ScriptFrameIter iter(cx); !iter.done(); ++iter) { - pcOffsets.append(uint32_t(iter.pc() - iter.script()->code)); - if (iter.isConstructing() && - iter.callee() == newScript->fun && - iter.thisv().isObject() && - !iter.thisv().toObject().hasLazyType() && - iter.thisv().toObject().type() == this) - { - RootedObject obj(cx, &iter.thisv().toObject()); + if (cx->isJSContext()) { + Vector pcOffsets(cx); + for (ScriptFrameIter iter(cx->asJSContext()); !iter.done(); ++iter) { + pcOffsets.append(uint32_t(iter.pc() - iter.script()->code)); + if (iter.isConstructing() && + iter.callee() == newScript->fun && + iter.thisv().isObject() && + !iter.thisv().toObject().hasLazyType() && + iter.thisv().toObject().type() == this) + { + RootedObject obj(cx, &iter.thisv().toObject()); - /* Whether all identified 'new' properties have been initialized. */ - bool finished = false; + /* Whether all identified 'new' properties have been initialized. */ + bool finished = false; - /* If not finished, number of properties that have been added. */ - uint32_t numProperties = 0; + /* If not finished, number of properties that have been added. */ + uint32_t numProperties = 0; - /* - * If non-zero, we are scanning initializers in a call which has - * already finished. - */ - size_t depth = 0; - size_t callDepth = pcOffsets.length() - 1; - uint32_t offset = pcOffsets[callDepth]; + /* + * If non-zero, we are scanning initializers in a call which has + * already finished. + */ + size_t depth = 0; + size_t callDepth = pcOffsets.length() - 1; + uint32_t offset = pcOffsets[callDepth]; - for (TypeNewScript::Initializer *init = newScript->initializerList;; init++) { - if (init->kind == TypeNewScript::Initializer::SETPROP) { - if (!depth && init->offset > offset) { - /* Advanced past all properties which have been initialized. */ - break; - } - numProperties++; - } else if (init->kind == TypeNewScript::Initializer::FRAME_PUSH) { - if (depth) { - depth++; - } else if (init->offset > offset) { - /* Advanced past all properties which have been initialized. */ - break; - } else if (init->offset == offset) { - if (!callDepth) + for (TypeNewScript::Initializer *init = newScript->initializerList;; init++) { + if (init->kind == TypeNewScript::Initializer::SETPROP) { + if (!depth && init->offset > offset) { + /* Advanced past all properties which have been initialized. */ break; - offset = pcOffsets[--callDepth]; + } + numProperties++; + } else if (init->kind == TypeNewScript::Initializer::FRAME_PUSH) { + if (depth) { + depth++; + } else if (init->offset > offset) { + /* Advanced past all properties which have been initialized. */ + break; + } else if (init->offset == offset) { + if (!callDepth) + break; + offset = pcOffsets[--callDepth]; + } else { + /* This call has already finished. */ + depth = 1; + } + } else if (init->kind == TypeNewScript::Initializer::FRAME_POP) { + if (depth) { + depth--; + } else { + /* This call has not finished yet. */ + break; + } } else { - /* This call has already finished. */ - depth = 1; - } - } else if (init->kind == TypeNewScript::Initializer::FRAME_POP) { - if (depth) { - depth--; - } else { - /* This call has not finished yet. */ + JS_ASSERT(init->kind == TypeNewScript::Initializer::DONE); + finished = true; break; } - } else { - JS_ASSERT(init->kind == TypeNewScript::Initializer::DONE); - finished = true; - break; } - } - if (!finished) - obj->rollbackProperties(cx, numProperties); + if (!finished) + obj->rollbackProperties(cx, numProperties); + } } + } else { + // Threads with an ExclusiveContext are not allowed to run scripts. + JS_ASSERT(!cx->perThreadData->activation()); } /* We NULL out newScript *before* freeing it so the write barrier works. */ @@ -5029,7 +5041,7 @@ AnalyzePoppedThis(JSContext *cx, SSAUseChain *use, if (shape && shape->hasSlot()) { Value protov = type->proto->getSlot(shape->slot()); TypeSet *types = TypeScript::BytecodeTypes(script, pc); - types->addType(cx, GetValueType(cx, protov)); + types->addType(cx, GetValueType(protov)); } return true; @@ -5530,7 +5542,7 @@ types::TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const return; } - Type type = GetValueType(cx, rval); + Type type = GetValueType(rval); TypeSet *types = TypeScript::BytecodeTypes(script, pc); if (types->hasType(type)) return; @@ -5804,14 +5816,12 @@ JSScript::makeAnalysis(JSContext *cx) } /* static */ bool -JSFunction::setTypeForScriptedFunction(ExclusiveContext *cxArg, HandleFunction fun, +JSFunction::setTypeForScriptedFunction(ExclusiveContext *cx, HandleFunction fun, bool singleton /* = false */) { - if (!cxArg->typeInferenceEnabled()) + if (!cx->typeInferenceEnabled()) return true; - JSContext *cx = cxArg->asJSContext(); - if (singleton) { if (!setSingletonType(cx, fun)) return false; @@ -6051,7 +6061,7 @@ ExclusiveContext::getNewType(Class *clasp, TaggedProto proto_, JSFunction *fun_) * 'prototype' property of some scripted function. */ if (type->newScript && type->newScript->fun != fun_) - type->clearNewScript(asJSContext()); + type->clearNewScript(this); return type; } @@ -6077,8 +6087,7 @@ ExclusiveContext::getNewType(Class *clasp, TaggedProto proto_, JSFunction *fun_) if (!typeInferenceEnabled()) return type; - JSContext *cx = asJSContext(); - AutoEnterAnalysis enter(cx); + AutoEnterAnalysis enter(this); /* * Set the special equality flag for types whose prototype also has the @@ -6086,22 +6095,22 @@ ExclusiveContext::getNewType(Class *clasp, TaggedProto proto_, JSFunction *fun_) * types and the possible js::Class of objects with that type. */ if (proto.isObject()) { - RootedObject obj(cx, proto.toObject()); + RootedObject obj(this, proto.toObject()); if (fun) - CheckNewScriptProperties(cx, type, fun); + CheckNewScriptProperties(asJSContext(), type, fun); if (obj->is()) { - AddTypeProperty(cx, type, "source", types::Type::StringType()); - AddTypeProperty(cx, type, "global", types::Type::BooleanType()); - AddTypeProperty(cx, type, "ignoreCase", types::Type::BooleanType()); - AddTypeProperty(cx, type, "multiline", types::Type::BooleanType()); - AddTypeProperty(cx, type, "sticky", types::Type::BooleanType()); - AddTypeProperty(cx, type, "lastIndex", types::Type::Int32Type()); + AddTypeProperty(this, type, "source", types::Type::StringType()); + AddTypeProperty(this, type, "global", types::Type::BooleanType()); + AddTypeProperty(this, type, "ignoreCase", types::Type::BooleanType()); + AddTypeProperty(this, type, "multiline", types::Type::BooleanType()); + AddTypeProperty(this, type, "sticky", types::Type::BooleanType()); + AddTypeProperty(this, type, "lastIndex", types::Type::Int32Type()); } if (obj->is()) - AddTypeProperty(cx, type, "length", Type::Int32Type()); + AddTypeProperty(this, type, "length", Type::Int32Type()); } /* diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index dda984dbe776..3cfc66369aa2 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -204,7 +204,7 @@ class Type }; /* Get the type of a jsval, or zero for an unknown special value. */ -inline Type GetValueType(JSContext *cx, const Value &val); +inline Type GetValueType(const Value &val); /* * Type inference memory management overview. @@ -489,10 +489,10 @@ class TypeSet * Add a type to this set, calling any constraint handlers if this is a new * possible type. */ - inline void addType(JSContext *cx, Type type); + inline void addType(ExclusiveContext *cx, Type type); /* Mark this type set as representing an own property or configured property. */ - inline void setOwnProperty(JSContext *cx, bool configured); + inline void setOwnProperty(ExclusiveContext *cx, bool configured); /* * Add an object to this set using the specified allocator, without @@ -1040,10 +1040,10 @@ struct TypeObject : gc::Cell * assignment, and the own types of the property will be used instead of * aggregate types. */ - inline HeapTypeSet *getProperty(JSContext *cx, jsid id, bool own); + inline HeapTypeSet *getProperty(ExclusiveContext *cx, jsid id, bool own); /* Get a property only if it already exists. */ - inline HeapTypeSet *maybeGetProperty(jsid id, JSContext *cx); + inline HeapTypeSet *maybeGetProperty(ExclusiveContext *cx, jsid id); inline unsigned getPropertyCount(); inline Property *getProperty(unsigned i); @@ -1056,19 +1056,19 @@ struct TypeObject : gc::Cell /* Helpers */ - bool addProperty(JSContext *cx, jsid id, Property **pprop); - bool addDefiniteProperties(JSContext *cx, JSObject *obj); + bool addProperty(ExclusiveContext *cx, jsid id, Property **pprop); + bool addDefiniteProperties(ExclusiveContext *cx, JSObject *obj); bool matchDefiniteProperties(HandleObject obj); void addPrototype(JSContext *cx, TypeObject *proto); - void addPropertyType(JSContext *cx, jsid id, Type type); - void addPropertyType(JSContext *cx, jsid id, const Value &value); - void addPropertyType(JSContext *cx, const char *name, Type type); - void addPropertyType(JSContext *cx, const char *name, const Value &value); - void markPropertyConfigured(JSContext *cx, jsid id); - void markStateChange(JSContext *cx); - void setFlags(JSContext *cx, TypeObjectFlags flags); - void markUnknown(JSContext *cx); - void clearNewScript(JSContext *cx); + void addPropertyType(ExclusiveContext *cx, jsid id, Type type); + void addPropertyType(ExclusiveContext *cx, jsid id, const Value &value); + void addPropertyType(ExclusiveContext *cx, const char *name, Type type); + void addPropertyType(ExclusiveContext *cx, const char *name, const Value &value); + void markPropertyConfigured(ExclusiveContext *cx, jsid id); + void markStateChange(ExclusiveContext *cx); + void setFlags(ExclusiveContext *cx, TypeObjectFlags flags); + void markUnknown(ExclusiveContext *cx); + void clearNewScript(ExclusiveContext *cx); void getFromPrototypes(JSContext *cx, jsid id, TypeSet *types, bool force = false); void print(); @@ -1368,12 +1368,12 @@ struct TypeCompartment ObjectTypeTable *objectTypeTable; private: - void setTypeToHomogenousArray(JSContext *cx, JSObject *obj, Type type); + void setTypeToHomogenousArray(ExclusiveContext *cx, JSObject *obj, Type type); public: - void fixArrayType(JSContext *cx, JSObject *obj); - void fixObjectType(JSContext *cx, JSObject *obj); - void fixRestArgumentsType(JSContext *cx, JSObject *obj); + void fixArrayType(ExclusiveContext *cx, JSObject *obj); + void fixObjectType(ExclusiveContext *cx, JSObject *obj); + void fixRestArgumentsType(ExclusiveContext *cx, JSObject *obj); JSObject *newTypedObject(JSContext *cx, IdValuePair *properties, size_t nproperties); @@ -1414,7 +1414,7 @@ struct TypeCompartment void processPendingRecompiles(FreeOp *fop); /* Mark all types as needing destruction once inference has 'finished'. */ - void setPendingNukeTypes(JSContext *cx); + void setPendingNukeTypes(ExclusiveContext *cx); /* Mark a script as needing recompilation once inference has finished. */ void addPendingRecompile(JSContext *cx, const RecompileInfo &info); diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h index 2028f065e003..f2aebf853f56 100644 --- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -185,9 +185,8 @@ Type::ObjectType(TypeObjectKey *obj) } inline Type -GetValueType(JSContext *cx, const Value &val) +GetValueType(const Value &val) { - JS_ASSERT(cx->typeInferenceEnabled()); if (val.isDouble()) return Type::DoubleType(); if (val.isObject()) @@ -310,10 +309,10 @@ struct AutoEnterAnalysis JSCompartment *compartment; bool oldActiveAnalysis; - AutoEnterAnalysis(JSContext *cx) + AutoEnterAnalysis(ExclusiveContext *cx) : suppressGC(cx) { - init(cx->runtime()->defaultFreeOp(), cx->compartment()); + init(cx->defaultFreeOp(), cx->compartment()); } AutoEnterAnalysis(FreeOp *fop, JSCompartment *comp) @@ -535,7 +534,7 @@ TrackPropertyTypes(ExclusiveContext *cx, JSObject *obj, jsid id) if (!cx->typeInferenceEnabled() || obj->hasLazyType() || obj->type()->unknownProperties()) return false; - if (obj->hasSingletonType() && !obj->type()->maybeGetProperty(id, cx->asJSContext())) + if (obj->hasSingletonType() && !obj->type()->maybeGetProperty(cx, id)) return false; return true; @@ -566,7 +565,7 @@ AddTypePropertyId(ExclusiveContext *cx, JSObject *obj, jsid id, Type type) if (cx->typeInferenceEnabled()) { id = IdToTypeId(id); if (TrackPropertyTypes(cx, obj, id)) - obj->type()->addPropertyType(cx->asJSContext(), id, type); + obj->type()->addPropertyType(cx, id, type); } } @@ -576,19 +575,19 @@ AddTypePropertyId(ExclusiveContext *cx, JSObject *obj, jsid id, const Value &val if (cx->typeInferenceEnabled()) { id = IdToTypeId(id); if (TrackPropertyTypes(cx, obj, id)) - obj->type()->addPropertyType(cx->asJSContext(), id, value); + obj->type()->addPropertyType(cx, id, value); } } inline void -AddTypeProperty(JSContext *cx, TypeObject *obj, const char *name, Type type) +AddTypeProperty(ExclusiveContext *cx, TypeObject *obj, const char *name, Type type) { if (cx->typeInferenceEnabled() && !obj->unknownProperties()) obj->addPropertyType(cx, name, type); } inline void -AddTypeProperty(JSContext *cx, TypeObject *obj, const char *name, const Value &value) +AddTypeProperty(ExclusiveContext *cx, TypeObject *obj, const char *name, const Value &value) { if (cx->typeInferenceEnabled() && !obj->unknownProperties()) obj->addPropertyType(cx, name, value); @@ -599,7 +598,7 @@ inline void MarkTypeObjectFlags(ExclusiveContext *cx, JSObject *obj, TypeObjectFlags flags) { if (cx->typeInferenceEnabled() && !obj->hasLazyType() && !obj->type()->hasAllFlags(flags)) - obj->type()->setFlags(cx->asJSContext(), flags); + obj->type()->setFlags(cx, flags); } /* @@ -630,7 +629,7 @@ MarkTypePropertyConfigured(ExclusiveContext *cx, HandleObject obj, jsid id) if (cx->typeInferenceEnabled()) { id = IdToTypeId(id); if (TrackPropertyTypes(cx, obj, id)) - obj->type()->markPropertyConfigured(cx->asJSContext(), id); + obj->type()->markPropertyConfigured(cx, id); } } @@ -639,7 +638,7 @@ inline void MarkObjectStateChange(ExclusiveContext *cx, JSObject *obj) { if (cx->typeInferenceEnabled() && !obj->hasLazyType() && !obj->type()->unknownProperties()) - obj->type()->markStateChange(cx->asJSContext()); + obj->type()->markStateChange(cx); } /* @@ -648,23 +647,17 @@ MarkObjectStateChange(ExclusiveContext *cx, JSObject *obj) */ inline void -FixArrayType(ExclusiveContext *cxArg, HandleObject obj) +FixArrayType(ExclusiveContext *cx, HandleObject obj) { - if (cxArg->isJSContext()) { - JSContext *cx = cxArg->asJSContext(); - if (cx->typeInferenceEnabled()) - cx->compartment()->types.fixArrayType(cx, obj); - } + if (cx->typeInferenceEnabled()) + cx->compartment()->types.fixArrayType(cx, obj); } inline void -FixObjectType(ExclusiveContext *cxArg, HandleObject obj) +FixObjectType(ExclusiveContext *cx, HandleObject obj) { - if (cxArg->isJSContext()) { - JSContext *cx = cxArg->asJSContext(); - if (cx->typeInferenceEnabled()) - cx->compartment()->types.fixObjectType(cx, obj); - } + if (cx->typeInferenceEnabled()) + cx->compartment()->types.fixObjectType(cx, obj); } /* Interface helpers for JSScript*. */ @@ -970,7 +963,7 @@ TypeScript::SetThis(JSContext *cx, JSScript *script, Type type) TypeScript::SetThis(JSContext *cx, JSScript *script, const js::Value &value) { if (cx->typeInferenceEnabled()) - SetThis(cx, script, GetValueType(cx, value)); + SetThis(cx, script, GetValueType(value)); } /* static */ inline void @@ -992,7 +985,7 @@ TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, Type type TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js::Value &value) { if (cx->typeInferenceEnabled()) { - Type type = GetValueType(cx, value); + Type type = GetValueType(value); SetArgument(cx, script, arg, type); } } @@ -1280,9 +1273,9 @@ TypeSet::clearObjects() } inline void -TypeSet::addType(JSContext *cx, Type type) +TypeSet::addType(ExclusiveContext *cxArg, Type type) { - JS_ASSERT(cx->compartment()->activeAnalysis); + JS_ASSERT(cxArg->compartment()->activeAnalysis); if (unknown()) return; @@ -1308,14 +1301,14 @@ TypeSet::addType(JSContext *cx, Type type) goto unknownObject; LifoAlloc &alloc = - purged() ? cx->compartment()->analysisLifoAlloc : cx->typeLifoAlloc(); + purged() ? cxArg->compartment()->analysisLifoAlloc : cxArg->typeLifoAlloc(); uint32_t objectCount = baseObjectCount(); TypeObjectKey *object = type.objectKey(); TypeObjectKey **pentry = HashSetInsert (alloc, objectSet, objectCount, object); if (!pentry) { - cx->compartment()->types.setPendingNukeTypes(cx); + cxArg->compartment()->types.setPendingNukeTypes(cxArg); return; } if (*pentry) @@ -1347,17 +1340,20 @@ TypeSet::addType(JSContext *cx, Type type) TypeString(type)); /* Propagate the type to all constraints. */ - TypeConstraint *constraint = constraintList; - while (constraint) { - cx->compartment()->types.addPending(cx, constraint, this, type); - constraint = constraint->next; + if (JSContext *cx = cxArg->maybeJSContext()) { + TypeConstraint *constraint = constraintList; + while (constraint) { + cx->compartment()->types.addPending(cx, constraint, this, type); + constraint = constraint->next; + } + cx->compartment()->types.resolvePending(cx); + } else { + JS_ASSERT(!constraintList); } - - cx->compartment()->types.resolvePending(cx); } inline void -TypeSet::setOwnProperty(JSContext *cx, bool configured) +TypeSet::setOwnProperty(ExclusiveContext *cxArg, bool configured) { TypeFlags nflags = TYPE_FLAG_OWN_PROPERTY | (configured ? TYPE_FLAG_CONFIGURED_PROPERTY : 0); @@ -1367,10 +1363,14 @@ TypeSet::setOwnProperty(JSContext *cx, bool configured) flags |= nflags; /* Propagate the change to all constraints. */ - TypeConstraint *constraint = constraintList; - while (constraint) { - constraint->newPropertyState(cx, this); - constraint = constraint->next; + if (JSContext *cx = cxArg->maybeJSContext()) { + TypeConstraint *constraint = constraintList; + while (constraint) { + constraint->newPropertyState(cx, this); + constraint = constraint->next; + } + } else { + JS_ASSERT(!constraintList); } } @@ -1476,7 +1476,7 @@ TypeObject::setBasePropertyCount(uint32_t count) } inline HeapTypeSet * -TypeObject::getProperty(JSContext *cx, jsid id, bool own) +TypeObject::getProperty(ExclusiveContext *cx, jsid id, bool own) { JS_ASSERT(cx->compartment()->activeAnalysis); @@ -1524,7 +1524,7 @@ TypeObject::getProperty(JSContext *cx, jsid id, bool own) } inline HeapTypeSet * -TypeObject::maybeGetProperty(jsid id, JSContext *cx) +TypeObject::maybeGetProperty(ExclusiveContext *cx, jsid id) { JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id)); JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id)); diff --git a/js/src/jsobj.h b/js/src/jsobj.h index cf13160a806e..2247915a47e0 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -377,7 +377,7 @@ class JSObject : public js::ObjectImpl inline void prepareSlotRangeForOverwrite(size_t start, size_t end); inline void prepareElementRangeForOverwrite(size_t start, size_t end); - void rollbackProperties(JSContext *cx, uint32_t slotSpan); + void rollbackProperties(js::ExclusiveContext *cx, uint32_t slotSpan); inline void nativeSetSlot(uint32_t slot, const js::Value &value); static inline void nativeSetSlotWithType(js::ExclusiveContext *cx, diff --git a/js/src/jsworkers.cpp b/js/src/jsworkers.cpp index 3a10377accda..8179f3a2f353 100644 --- a/js/src/jsworkers.cpp +++ b/js/src/jsworkers.cpp @@ -226,12 +226,7 @@ js::StartOffThreadParseScript(JSContext *cx, const CompileOptions &options, if (!global) return false; - // For now, type inference is always disabled in exclusive zones, as type - // inference data is not merged between zones when finishing the off thread - // parse. This restriction would be fairly easy to lift. - JS_ASSERT(!cx->typeInferenceEnabled()); - global->zone()->types.inferenceEnabled = false; - + global->zone()->types.inferenceEnabled = cx->typeInferenceEnabled(); JS_SetCompartmentPrincipals(global->compartment(), cx->compartment()->principals); RootedObject obj(cx); diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index a374cadc48ff..81e32a025407 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -407,7 +407,9 @@ JSRuntime::~JSRuntime() PR_DestroyLock(exclusiveAccessLock); JS_ASSERT(!numExclusiveThreads); - exclusiveThreadsPaused = true; // Avoid bogus asserts during teardown. + + // Avoid bogus asserts during teardown. + exclusiveThreadsPaused = true; #endif /* diff --git a/js/src/vm/Shape.cpp b/js/src/vm/Shape.cpp index b10aeccffbec..4ea9e3e232bf 100644 --- a/js/src/vm/Shape.cpp +++ b/js/src/vm/Shape.cpp @@ -938,7 +938,7 @@ JSObject::clear(JSContext *cx, HandleObject obj) } void -JSObject::rollbackProperties(JSContext *cx, uint32_t slotSpan) +JSObject::rollbackProperties(ExclusiveContext *cx, uint32_t slotSpan) { /* * Remove properties from this object until it has a matching slot span. From e6289f59db2bc8b22a2c7a54956702e55d619845 Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Thu, 22 Aug 2013 07:22:33 -0600 Subject: [PATCH 38/54] Bug 907777 - Add preference for off thread parsing, coalesce with preference for off thread Ion compilation, r=billm. --- dom/base/nsJSEnvironment.cpp | 5 ++++- js/src/jit/AsmJS.cpp | 2 +- js/src/jit/Ion.cpp | 4 ++-- js/src/jit/Ion.h | 8 -------- js/src/jit/IonBuilder.cpp | 3 --- js/src/jit/IonSpewer.cpp | 10 ++++++---- js/src/jit/shared/Assembler-shared.h | 6 ++++-- js/src/jsapi.cpp | 26 ++++++++++++++++---------- js/src/jsapi.h | 8 +++++--- js/src/jsworkers.h | 8 ++++---- js/src/shell/js.cpp | 12 ++++++++---- js/src/vm/Runtime.cpp | 4 +++- js/src/vm/Runtime.h | 18 ++++++++++++++++++ modules/libpref/src/init/all.js | 1 + 14 files changed, 72 insertions(+), 43 deletions(-) diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index c8019f5cc090..9f373424f9b2 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -698,6 +698,7 @@ static const char js_baselinejit_eager_str[] = JS_OPTIONS_DOT_STR "baselinejit. static const char js_ion_content_str[] = JS_OPTIONS_DOT_STR "ion.content"; static const char js_ion_chrome_str[] = JS_OPTIONS_DOT_STR "ion.chrome"; static const char js_ion_eager_str[] = JS_OPTIONS_DOT_STR "ion.unsafe_eager_compilation"; +static const char js_parallel_parsing_str[] = JS_OPTIONS_DOT_STR "parallel_parsing"; static const char js_ion_parallel_compilation_str[] = JS_OPTIONS_DOT_STR "ion.parallel_compilation"; int @@ -739,6 +740,7 @@ nsJSContext::JSOptionChangedCallback(const char *pref, void *data) js_ion_content_str); bool useIonEager = Preferences::GetBool(js_ion_eager_str); bool useAsmJS = Preferences::GetBool(js_asmjs_content_str); + bool parallelParsing = Preferences::GetBool(js_parallel_parsing_str); bool parallelIonCompilation = Preferences::GetBool(js_ion_parallel_compilation_str); nsCOMPtr xr = do_GetService(XULRUNTIME_SERVICE_CONTRACTID); if (xr) { @@ -793,7 +795,8 @@ nsJSContext::JSOptionChangedCallback(const char *pref, void *data) ::JS_SetOptions(context->mContext, newDefaultJSOptions & JSOPTION_MASK); - ::JS_SetParallelCompilationEnabled(context->mContext, parallelIonCompilation); + ::JS_SetParallelParsingEnabled(context->mContext, parallelParsing); + ::JS_SetParallelIonCompilationEnabled(context->mContext, parallelIonCompilation); ::JS_SetGlobalCompilerOption(context->mContext, JSCOMPILER_BASELINE_USECOUNT_TRIGGER, (useBaselineJITEager ? 0 : -1)); diff --git a/js/src/jit/AsmJS.cpp b/js/src/jit/AsmJS.cpp index 2a17102b3bdd..3b71a57e0746 100644 --- a/js/src/jit/AsmJS.cpp +++ b/js/src/jit/AsmJS.cpp @@ -4819,7 +4819,7 @@ ParallelCompilationEnabled(ExclusiveContext *cx) if (!cx->isJSContext()) return cx->workerThreadState()->numThreads > 1; - return OffThreadCompilationEnabled(cx->asJSContext()); + return OffThreadIonCompilationEnabled(cx->asJSContext()->runtime()); } // State of compilation as tracked and updated by the main thread. diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index f55b0210e396..9fc18f553113 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -1592,7 +1592,7 @@ OffThreadCompilationAvailable(JSContext *cx) // Also skip off thread compilation if the SPS profiler is enabled, as it // stores strings in the spsProfiler data structure, which is not protected // by a lock. - return OffThreadCompilationEnabled(cx) + return OffThreadIonCompilationEnabled(cx->runtime()) && cx->runtime()->gcIncrementalState == gc::NO_INCREMENTAL && !cx->runtime()->profilingScripts && !cx->runtime()->spsProfiler.enabled(); @@ -1773,7 +1773,7 @@ CheckScriptSize(JSContext *cx, JSScript* script) if (script->length > MAX_MAIN_THREAD_SCRIPT_SIZE || numLocalsAndArgs > MAX_MAIN_THREAD_LOCALS_AND_ARGS) { - if (OffThreadCompilationEnabled(cx)) { + if (OffThreadIonCompilationEnabled(cx->runtime())) { // Even if off thread compilation is enabled, there are cases where // compilation must still occur on the main thread. Don't compile // in these cases (except when profiling scripts, as compilations diff --git a/js/src/jit/Ion.h b/js/src/jit/Ion.h index df75c60e7162..8d6a32cd6e00 100644 --- a/js/src/jit/Ion.h +++ b/js/src/jit/Ion.h @@ -93,11 +93,6 @@ struct IonOptions // Default: true bool eaa; - // Toggles whether compilation occurs off the main thread. - // - // Default: true iff there are at least two CPUs available - bool parallelCompilation; - #ifdef CHECK_OSIPOINT_REGISTERS // Emit extra code to verify live regs at the start of a VM call // are not modified before its OsiPoint. @@ -204,8 +199,6 @@ struct IonOptions eagerCompilation = true; usesBeforeCompile = 0; baselineUsesBeforeCompile = 0; - - parallelCompilation = false; } IonOptions() @@ -221,7 +214,6 @@ struct IonOptions checkRangeAnalysis(false), uce(true), eaa(true), - parallelCompilation(false), #ifdef CHECK_OSIPOINT_REGISTERS checkOsiPointRegisters(false), #endif diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 40067ff6731b..5027cef58931 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -9038,9 +9038,6 @@ IonBuilder::addShapeGuard(MDefinition *obj, Shape *const shape, BailoutKind bail types::StackTypeSet * IonBuilder::cloneTypeSet(types::StackTypeSet *types) { - if (!js_IonOptions.parallelCompilation) - return types; - // Clone a type set so that it can be stored into the MIR and accessed // during off thread compilation. This is necessary because main thread // updates to type sets can race with reads in the compiler backend, and diff --git a/js/src/jit/IonSpewer.cpp b/js/src/jit/IonSpewer.cpp index e801e95e81be..c98218a32e74 100644 --- a/js/src/jit/IonSpewer.cpp +++ b/js/src/jit/IonSpewer.cpp @@ -8,6 +8,8 @@ #include "jit/IonSpewer.h" +#include "jsworkers.h" + #include "jit/Ion.h" #ifndef ION_SPEW_DIR @@ -79,7 +81,7 @@ ion::EnableIonDebugLogging() void ion::IonSpewNewFunction(MIRGraph *graph, HandleScript func) { - if (!js_IonOptions.parallelCompilation) { + if (!OffThreadIonCompilationEnabled(GetIonContext()->runtime)) { ionspewer.beginFunction(graph, func); } else { if (func) { @@ -96,21 +98,21 @@ ion::IonSpewNewFunction(MIRGraph *graph, HandleScript func) void ion::IonSpewPass(const char *pass) { - if (!js_IonOptions.parallelCompilation) + if (!OffThreadIonCompilationEnabled(GetIonContext()->runtime)) ionspewer.spewPass(pass); } void ion::IonSpewPass(const char *pass, LinearScanAllocator *ra) { - if (!js_IonOptions.parallelCompilation) + if (!OffThreadIonCompilationEnabled(GetIonContext()->runtime)) ionspewer.spewPass(pass, ra); } void ion::IonSpewEndFunction() { - if (!js_IonOptions.parallelCompilation) + if (!OffThreadIonCompilationEnabled(GetIonContext()->runtime)) ionspewer.endFunction(); } diff --git a/js/src/jit/shared/Assembler-shared.h b/js/src/jit/shared/Assembler-shared.h index 23292291c136..e80aa22c7318 100644 --- a/js/src/jit/shared/Assembler-shared.h +++ b/js/src/jit/shared/Assembler-shared.h @@ -11,6 +11,8 @@ #include +#include "jsworkers.h" + #include "jit/IonAllocPolicy.h" #include "jit/Registers.h" #include "jit/RegisterSets.h" @@ -269,8 +271,8 @@ class Label : public LabelBase #ifdef DEBUG // Note: the condition is a hack to silence this assert when OOM testing, // see bug 756614. - if (!js_IonOptions.parallelCompilation) - JS_ASSERT_IF(MaybeGetIonContext() && !GetIonContext()->runtime->hadOutOfMemory, !used()); + if (MaybeGetIonContext() && !OffThreadIonCompilationEnabled(GetIonContext()->runtime)) + JS_ASSERT_IF(!GetIonContext()->runtime->hadOutOfMemory, !used()); #endif } }; diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index e667fdfe90cf..11ec92869540 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -4839,10 +4839,13 @@ JS::Compile(JSContext *cx, HandleObject obj, CompileOptions options, const char JS_PUBLIC_API(bool) JS::CanCompileOffThread(JSContext *cx, const CompileOptions &options) { -#if defined(JS_THREADSAFE) && defined(JS_ION) +#ifdef JS_WORKER_THREADS if (!cx->runtime()->useHelperThreads() || !cx->runtime()->helperThreadCount()) return false; + if (!cx->runtime()->useHelperThreadsForParsing()) + return false; + // Off thread compilation can't occur during incremental collections on the // atoms compartment, to avoid triggering barriers. Outside the atoms // compartment, the compilation will use a new zone which doesn't require @@ -4880,7 +4883,7 @@ JS::CompileOffThread(JSContext *cx, Handle obj, CompileOptions option const jschar *chars, size_t length, OffThreadCompileCallback callback, void *callbackData) { -#if defined(JS_THREADSAFE) && defined(JS_ION) +#ifdef JS_WORKER_THREADS JS_ASSERT(CanCompileOffThread(cx, options)); return StartOffThreadParseScript(cx, options, chars, length, obj, callback, callbackData); #else @@ -4891,7 +4894,7 @@ JS::CompileOffThread(JSContext *cx, Handle obj, CompileOptions option JS_PUBLIC_API(void) JS::FinishOffThreadScript(JSRuntime *rt, JSScript *script) { -#if defined(JS_THREADSAFE) && defined(JS_ION) +#ifdef JS_WORKER_THREADS JS_ASSERT(CurrentThreadCanAccessRuntime(rt)); rt->workerThreadState->finishParseTaskForScript(rt, script); #else @@ -6636,10 +6639,18 @@ JS_ScheduleGC(JSContext *cx, uint32_t count) #endif JS_PUBLIC_API(void) -JS_SetParallelCompilationEnabled(JSContext *cx, bool enabled) +JS_SetParallelParsingEnabled(JSContext *cx, bool enabled) { #ifdef JS_ION - ion::js_IonOptions.parallelCompilation = enabled; + cx->runtime()->setCanUseHelperThreadsForParsing(enabled); +#endif +} + +JS_PUBLIC_API(void) +JS_SetParallelIonCompilationEnabled(JSContext *cx, bool enabled) +{ +#ifdef JS_ION + cx->runtime()->setCanUseHelperThreadsForIonCompilation(enabled); #endif } @@ -6661,11 +6672,6 @@ JS_SetGlobalCompilerOption(JSContext *cx, JSCompilerOption opt, uint32_t value) ion::js_IonOptions.usesBeforeCompile = value; ion::js_IonOptions.eagerCompilation = (value == 0); break; - case JSCOMPILER_PJS_ENABLE: - if (value == uint32_t(-1)) - value = uint32_t(defaultValues.parallelCompilation); - ion::js_IonOptions.parallelCompilation = bool(value); - break; } #endif } diff --git a/js/src/jsapi.h b/js/src/jsapi.h index f1c9f61c7327..c103ec36787b 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -5196,12 +5196,14 @@ JS_ScheduleGC(JSContext *cx, uint32_t count); #endif extern JS_PUBLIC_API(void) -JS_SetParallelCompilationEnabled(JSContext *cx, bool enabled); +JS_SetParallelParsingEnabled(JSContext *cx, bool enabled); + +extern JS_PUBLIC_API(void) +JS_SetParallelIonCompilationEnabled(JSContext *cx, bool enabled); typedef enum JSCompilerOption { JSCOMPILER_BASELINE_USECOUNT_TRIGGER, - JSCOMPILER_ION_USECOUNT_TRIGGER, - JSCOMPILER_PJS_ENABLE + JSCOMPILER_ION_USECOUNT_TRIGGER } JSCompilerOption; extern JS_PUBLIC_API(void) diff --git a/js/src/jsworkers.h b/js/src/jsworkers.h index 934670410285..0faf269ab650 100644 --- a/js/src/jsworkers.h +++ b/js/src/jsworkers.h @@ -186,12 +186,12 @@ struct WorkerThread #endif /* JS_THREADSAFE && JS_ION */ inline bool -OffThreadCompilationEnabled(JSContext *cx) +OffThreadIonCompilationEnabled(JSRuntime *rt) { #ifdef JS_WORKER_THREADS - return ion::js_IonOptions.parallelCompilation - && cx->runtime()->useHelperThreads() - && cx->runtime()->helperThreadCount() != 0; + return rt->useHelperThreads() + && rt->helperThreadCount() != 0 + && rt->useHelperThreadsForIonCompilation(); #else return false; #endif diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index d2eb07399b33..a4f29ce6beb5 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -5151,19 +5151,23 @@ ProcessArgs(JSContext *cx, JSObject *obj_, OptionParser *op) ion::js_IonOptions.compileTryCatch = true; #ifdef JS_THREADSAFE + bool parallelCompilation = false; if (const char *str = op->getStringOption("ion-parallel-compile")) { if (strcmp(str, "on") == 0) { if (cx->runtime()->helperThreadCount() == 0) { fprintf(stderr, "Parallel compilation not available without helper threads"); return EXIT_FAILURE; } - ion::js_IonOptions.parallelCompilation = true; - } else if (strcmp(str, "off") == 0) { - ion::js_IonOptions.parallelCompilation = false; - } else { + parallelCompilation = true; + } else if (strcmp(str, "off") != 0) { return OptionFailure("ion-parallel-compile", str); } } + /* + * Note: In shell builds, parallel compilation is only enabled with an + * explicit option. + */ + cx->runtime()->setCanUseHelperThreadsForIonCompilation(parallelCompilation); #endif /* JS_THREADSAFE */ #endif /* JS_ION */ diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 81e32a025407..8244d09688e7 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -269,7 +269,9 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads) parallelWarmup(0), ionReturnOverride_(MagicValue(JS_ARG_POISON)), useHelperThreads_(useHelperThreads), - requestedHelperThreadCount(-1) + requestedHelperThreadCount(-1), + useHelperThreadsForIonCompilation_(true), + useHelperThreadsForParsing_(true) #ifdef DEBUG , enteredPolicy(NULL) #endif diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index e0fbd0b1d343..dda190bd005a 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -1576,6 +1576,10 @@ struct JSRuntime : public JS::shadow::Runtime, JSUseHelperThreads useHelperThreads_; int32_t requestedHelperThreadCount; + // Settings for how helper threads can be used. + bool useHelperThreadsForIonCompilation_; + bool useHelperThreadsForParsing_; + public: bool useHelperThreads() const { @@ -1601,6 +1605,20 @@ struct JSRuntime : public JS::shadow::Runtime, #endif } + void setCanUseHelperThreadsForIonCompilation(bool value) { + useHelperThreadsForIonCompilation_ = value; + } + bool useHelperThreadsForIonCompilation() const { + return useHelperThreadsForIonCompilation_; + } + + void setCanUseHelperThreadsForParsing(bool value) { + useHelperThreadsForParsing_ = value; + } + bool useHelperThreadsForParsing() const { + return useHelperThreadsForParsing_; + } + #ifdef DEBUG public: js::AutoEnterPolicy *enteredPolicy; diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index b7430818a22a..c66de11d75ce 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -821,6 +821,7 @@ pref("javascript.options.baselinejit.chrome", true); pref("javascript.options.ion.content", true); pref("javascript.options.ion.chrome", false); pref("javascript.options.asmjs", true); +pref("javascript.options.parallel_parsing", true); pref("javascript.options.ion.parallel_compilation", true); pref("javascript.options.jit_hardening", true); pref("javascript.options.typeinference.content", true); From 6415dbe041c24043cd25fc510c0da03d5bf10bd1 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Thu, 22 Aug 2013 09:26:19 -0400 Subject: [PATCH 39/54] Remove an extra include of nsContentUtils that I somehow added in bug 903819 --- content/html/content/src/HTMLAnchorElement.h | 1 - 1 file changed, 1 deletion(-) diff --git a/content/html/content/src/HTMLAnchorElement.h b/content/html/content/src/HTMLAnchorElement.h index 0e6938cef997..106066fa5c07 100644 --- a/content/html/content/src/HTMLAnchorElement.h +++ b/content/html/content/src/HTMLAnchorElement.h @@ -9,7 +9,6 @@ #include "mozilla/Attributes.h" #include "mozilla/dom/Link.h" -#include "nsContentUtils.h" #include "nsGenericHTMLElement.h" #include "nsIDOMHTMLAnchorElement.h" From 29eadf205f298cd8703b041507fa85dc74ade128 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Thu, 22 Aug 2013 09:27:04 -0400 Subject: [PATCH 40/54] Bug 883493: Make CGUnionConversionStruct use CGClass r=bz --- dom/bindings/Codegen.py | 115 ++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 58 deletions(-) diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index a7588278530d..b32331ce6dd0 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -5980,12 +5980,12 @@ def getUnionTypeTemplateVars(unionType, type, descriptorProvider, isReturnValue= else: name = type.name - tryNextCode = """tryNext = true; -return true;""" + tryNextCode = ("tryNext = true;\n" + "return true;") if type.isGeckoInterface(): - tryNextCode = ("""if (mUnion.mType != mUnion.eUninitialized) { - mUnion.Destroy%s(); -}""" % name) + tryNextCode + tryNextCode = ("if (mUnion.mType != mUnion.eUninitialized) {" + " mUnion.Destroy%s();" + "}" % name) + tryNextCode conversionInfo = getJSToNativeConversionInfo( type, descriptorProvider, failureCode=tryNextCode, isDefinitelyObject=True, isInUnionReturnValue=isReturnValue, @@ -5999,11 +5999,14 @@ return true;""" externalType = getUnionAccessorSignatureType(type, descriptorProvider).define() if type.isObject(): - setter = CGGeneric("void SetToObject(JSContext* cx, JSObject* obj)\n" - "{\n" - " mUnion.mValue.mObject.SetValue(cx, obj);\n" - " mUnion.mType = mUnion.eObject;\n" - "}") + body = ("mUnion.mValue.mObject.SetValue(cx, obj);\n" + "mUnion.mType = mUnion.eObject;") + setter = ClassMethod("SetToObject", "void", + [Argument("JSContext*", "cx"), + Argument("JSObject*", "obj")], + inline=True, bodyInHeader=True, + body=body) + else: jsConversion = string.Template(conversionInfo.template).substitute( { @@ -6014,20 +6017,22 @@ return true;""" } ) jsConversion = CGWrapper(CGGeneric(jsConversion), + pre="tryNext = false;\n", post="\n" "return true;") - setter = CGWrapper(CGIndenter(jsConversion), - pre="bool TrySetTo" + name + "(JSContext* cx, JS::Handle value, JS::MutableHandle pvalue, bool& tryNext)\n" - "{\n" - " tryNext = false;\n", - post="\n" - "}") + setter = ClassMethod("TrySetTo" + name, "bool", + [Argument("JSContext*", "cx"), + Argument("JS::Handle", "value"), + Argument("JS::MutableHandle", "pvalue"), + Argument("bool&", "tryNext")], + inline=True, bodyInHeader=True, + body=jsConversion.define()) return { "name": name, "structType": structType, "externalType": externalType, - "setter": CGIndenter(setter).define(), + "setter": setter, "holderType": conversionInfo.holderType.define() if conversionInfo.holderType else None } @@ -6164,51 +6169,45 @@ class CGUnionConversionStruct(CGThing): self.descriptorProvider = descriptorProvider def declare(self): - setters = [] + + structName = str(self.type) + members = [ClassMember("mUnion", structName + "&", + body="const_cast<%s&>(aUnion)" % structName)] + # Argument needs to be a const ref because that's all Maybe<> allows + ctor = ClassConstructor([Argument("const %s&" % structName, "aUnion")], + bodyInHeader=True, + visibility="public", + explicit=True) + methods = [] if self.type.hasNullableType: - setters.append(""" bool SetNull() - { - mUnion.mType = mUnion.eNull; - return true; - }""") + methods.append(ClassMethod("SetNull", "bool", [], + body=("mUnion.mType = mUnion.eNull;\n" + "return true;"), + inline=True, bodyInHeader=True)) - templateVars = map(lambda t: getUnionTypeTemplateVars(self.type, t, self.descriptorProvider), - self.type.flatMemberTypes) - structName = self.type.__str__() + for t in self.type.flatMemberTypes: + vars = getUnionTypeTemplateVars(self.type, + t, self.descriptorProvider) + methods.append(vars["setter"]) + if vars["name"] != "Object": + body=string.Template("mUnion.mType = mUnion.e${name};\n" + "return mUnion.mValue.m${name}.SetValue();").substitute(vars) + methods.append(ClassMethod("SetAs" + vars["name"], + vars["structType"] + "&", + [], + bodyInHeader=True, + body=body, + visibility="private")) + if vars["holderType"] is not None: + members.append(ClassMember("m%sHolder" % vars["name"], + vars["holderType"])) - setters.extend(mapTemplate("${setter}", templateVars)) - # Don't generate a SetAsObject, since we don't use it - private = "\n".join(mapTemplate(""" ${structType}& SetAs${name}() - { - mUnion.mType = mUnion.e${name}; - return mUnion.mValue.m${name}.SetValue(); - }""", - filter(lambda v: v["name"] != "Object", - templateVars))) - private += "\n\n" - holders = filter(lambda v: v["holderType"] is not None, templateVars) - if len(holders) > 0: - private += "\n".join(mapTemplate(" ${holderType} m${name}Holder;", holders)) - private += "\n\n" - private += " " + structName + "& mUnion;" - return string.Template(""" -class ${structName}Argument { -public: - // Argument needs to be a const ref because that's all Maybe<> allows - ${structName}Argument(const ${structName}& aUnion) : mUnion(const_cast<${structName}&>(aUnion)) - { - } - -${setters} - -private: -${private} -}; -""").substitute({"structName": structName, - "setters": "\n\n".join(setters), - "private": private - }) + return CGClass(structName + "Argument", + members=members, + constructors=[ctor], + methods=methods, + disallowCopyConstruction=True).declare() def define(self): return """ From f18609637899500f266919d4f5afe28a6d43e404 Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Thu, 22 Aug 2013 15:33:58 +0200 Subject: [PATCH 41/54] Bug 876096 - Try adding waitForFocus, let's see whether that fixes test_activation.xul. --- dom/tests/mochitest/chrome/window_activation.xul | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dom/tests/mochitest/chrome/window_activation.xul b/dom/tests/mochitest/chrome/window_activation.xul index cbfa548d7fdc..eb2d44ed45f1 100644 --- a/dom/tests/mochitest/chrome/window_activation.xul +++ b/dom/tests/mochitest/chrome/window_activation.xul @@ -21,15 +21,16 @@ var ok = window.opener.wrappedJSObject.ok; var complete = window.opener.wrappedJSObject.complete; var openerDoc = window.opener.wrappedJSObject.document; +var SimpleTest = window.opener.wrappedJSObject.SimpleTest; -window.onfocus = function () { +SimpleTest.waitForFocus(function () { ok(getComputedStyle(document.getElementById("box"), "").backgroundColor, "rgb(0, 0, 255)"); ok(getComputedStyle(openerDoc.getElementById("box"), "").backgroundColor, "rgb(0, 255, 255)"); window.opener.focus(); ok(getComputedStyle(document.getElementById("box"), "").backgroundColor, "rgb(0, 255, 255)"); ok(getComputedStyle(openerDoc.getElementById("box"), "").backgroundColor, "rgb(0, 0, 255)"); complete(); -} +}, window); ]]> From 37640717b247501b5326cc2195f5371bbd80a28b Mon Sep 17 00:00:00 2001 From: Masatoshi Kimura Date: Thu, 22 Aug 2013 22:44:19 +0900 Subject: [PATCH 42/54] Bug 600460 - Implement category entry removal for nsScriptNamespaceManager. r=bholley --- dom/base/nsScriptNameSpaceManager.cpp | 82 +++++++++++++++---- dom/base/nsScriptNameSpaceManager.h | 20 +++++ dom/tests/mochitest/bugs/test_bug641552.html | 73 +++++++++++++++-- .../specialpowers/content/specialpowersAPI.js | 6 ++ 4 files changed, 159 insertions(+), 22 deletions(-) diff --git a/dom/base/nsScriptNameSpaceManager.cpp b/dom/base/nsScriptNameSpaceManager.cpp index e15e4f5a7768..4d6b0cf7ffb7 100644 --- a/dom/base/nsScriptNameSpaceManager.cpp +++ b/dom/base/nsScriptNameSpaceManager.cpp @@ -174,6 +174,13 @@ nsScriptNameSpaceManager::AddToHash(PLDHashTable *aTable, const nsAString *aKey, return &entry->mGlobalName; } +void +nsScriptNameSpaceManager::RemoveFromHash(PLDHashTable *aTable, + const nsAString *aKey) +{ + PL_DHashTableOperate(aTable, aKey, PL_DHASH_REMOVE); +} + nsGlobalNameStruct* nsScriptNameSpaceManager::GetConstructorProto(const nsGlobalNameStruct* aStruct) { @@ -392,6 +399,7 @@ nsScriptNameSpaceManager::Init() if (serv) { serv->AddObserver(this, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID, true); + serv->AddObserver(this, NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID, true); } return NS_OK; @@ -615,10 +623,12 @@ nsScriptNameSpaceManager::RegisterDOMCIData(const char *aName, } nsresult -nsScriptNameSpaceManager::AddCategoryEntryToHash(nsICategoryManager* aCategoryManager, - const char* aCategory, - nsISupports* aEntry) +nsScriptNameSpaceManager::OperateCategoryEntryHash(nsICategoryManager* aCategoryManager, + const char* aCategory, + nsISupports* aEntry, + bool aRemove) { + MOZ_ASSERT(aCategoryManager); // Get the type from the category name. // NOTE: we could have passed the type in FillHash() and guessed it in // Observe() but this way, we have only one place to update and this is @@ -650,6 +660,31 @@ nsScriptNameSpaceManager::AddCategoryEntryToHash(nsICategoryManager* aCategoryMa nsresult rv = strWrapper->GetData(categoryEntry); NS_ENSURE_SUCCESS(rv, rv); + PLDHashTable *table; + if (type == nsGlobalNameStruct::eTypeNavigatorProperty) { + table = &mNavigatorNames; + } else { + table = &mGlobalNames; + } + + // We need to handle removal before calling GetCategoryEntry + // because the category entry is already removed before we are + // notified. + if (aRemove) { + NS_ConvertASCIItoUTF16 entry(categoryEntry); + const nsGlobalNameStruct *s = + type == nsGlobalNameStruct::eTypeNavigatorProperty ? + LookupNavigatorName(entry) : LookupNameInternal(entry); + // Verify mType so that this API doesn't remove names + // registered by others. + if (!s || s->mType != type) { + return NS_OK; + } + + RemoveFromHash(table, &entry); + return NS_OK; + } + nsXPIDLCString contractId; rv = aCategoryManager->GetCategoryEntry(aCategory, categoryEntry.get(), getter_Copies(contractId)); @@ -709,13 +744,6 @@ nsScriptNameSpaceManager::AddCategoryEntryToHash(nsICategoryManager* aCategoryMa } } - PLDHashTable *table; - if (type == nsGlobalNameStruct::eTypeNavigatorProperty) { - table = &mNavigatorNames; - } else { - table = &mGlobalNames; - } - nsGlobalNameStruct *s = AddToHash(table, categoryEntry.get()); NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY); @@ -732,6 +760,24 @@ nsScriptNameSpaceManager::AddCategoryEntryToHash(nsICategoryManager* aCategoryMa return NS_OK; } +nsresult +nsScriptNameSpaceManager::AddCategoryEntryToHash(nsICategoryManager* aCategoryManager, + const char* aCategory, + nsISupports* aEntry) +{ + return OperateCategoryEntryHash(aCategoryManager, aCategory, aEntry, + /* aRemove = */ false); +} + +nsresult +nsScriptNameSpaceManager::RemoveCategoryEntryFromHash(nsICategoryManager* aCategoryManager, + const char* aCategory, + nsISupports* aEntry) +{ + return OperateCategoryEntryHash(aCategoryManager, aCategory, aEntry, + /* aRemove = */ true); +} + NS_IMETHODIMP nsScriptNameSpaceManager::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData) @@ -740,7 +786,7 @@ nsScriptNameSpaceManager::Observe(nsISupports* aSubject, const char* aTopic, return NS_OK; } - if (strcmp(aTopic, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID) == 0) { + if (!strcmp(aTopic, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID)) { nsCOMPtr cm = do_GetService(NS_CATEGORYMANAGER_CONTRACTID); if (!cm) { @@ -749,11 +795,19 @@ nsScriptNameSpaceManager::Observe(nsISupports* aSubject, const char* aTopic, return AddCategoryEntryToHash(cm, NS_ConvertUTF16toUTF8(aData).get(), aSubject); + } else if (!strcmp(aTopic, NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID)) { + nsCOMPtr cm = + do_GetService(NS_CATEGORYMANAGER_CONTRACTID); + if (!cm) { + return NS_OK; + } + + return RemoveCategoryEntryFromHash(cm, NS_ConvertUTF16toUTF8(aData).get(), + aSubject); } - // TODO: we could observe NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID - // and NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID but we are safe without it. - // See bug 600460. + // TODO: we could observe NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID + // but we are safe without it. See bug 600460. return NS_OK; } diff --git a/dom/base/nsScriptNameSpaceManager.h b/dom/base/nsScriptNameSpaceManager.h index ad8b4a3ecaf3..966163dcb5a0 100644 --- a/dom/base/nsScriptNameSpaceManager.h +++ b/dom/base/nsScriptNameSpaceManager.h @@ -177,6 +177,8 @@ private: NS_ConvertASCIItoUTF16 key(aKey); return AddToHash(aTable, &key, aClassName); } + // Removes an existing entry from the hash. + void RemoveFromHash(PLDHashTable *aTable, const nsAString *aKey); nsresult FillHash(nsICategoryManager *aCategoryManager, const char *aCategory); @@ -197,6 +199,24 @@ private: const char* aCategory, nsISupports* aEntry); + /** + * Remove an existing category entry from the hash table. + * Only some categories can be removed (see the beginning of the definition). + * The other ones will be ignored. + * + * @aCategory Category where the entry will be removed from. + * @aEntry The entry that should be removed. + */ + nsresult RemoveCategoryEntryFromHash(nsICategoryManager* aCategoryManager, + const char* aCategory, + nsISupports* aEntry); + + // common helper for AddCategoryEntryToHash and RemoveCategoryEntryFromHash + nsresult OperateCategoryEntryHash(nsICategoryManager* aCategoryManager, + const char* aCategory, + nsISupports* aEntry, + bool aRemove); + nsGlobalNameStruct* LookupNameInternal(const nsAString& aName, const PRUnichar **aClassName = nullptr); diff --git a/dom/tests/mochitest/bugs/test_bug641552.html b/dom/tests/mochitest/bugs/test_bug641552.html index ca972d5f2a67..43122e34fdbd 100644 --- a/dom/tests/mochitest/bugs/test_bug641552.html +++ b/dom/tests/mochitest/bugs/test_bug641552.html @@ -19,22 +19,79 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=641552 SimpleTest.waitForExplicitFinish(); -SpecialPowers.addCategoryEntry("JavaScript-global-property", "randomname", "@mozilla.org/embedcomp/prompt-service;1", - false, true); +var categoryEntries = [ + {category: "JavaScript-global-property", entry: "randomname", contractId: "@mozilla.org/embedcomp/prompt-service;1"}, + {category: "JavaScript-navigator-property", entry: "randomname1", contractId: "@mozilla.org/embedcomp/prompt-service;1"}, + {category: "JavaScript-navigator-property", entry: "randomname2", contractId: "@mozilla.org/embedcomp/prompt-service;1"}, +]; -SpecialPowers.addCategoryEntry("JavaScript-navigator-property", "randomname1", "@mozilla.org/embedcomp/prompt-service;1", - false, true); +function addCategoryEntries(func) { + for (var categoryEntry of categoryEntries) { + SpecialPowers.addCategoryEntry(categoryEntry.category, categoryEntry.entry, categoryEntry.contractId, + false, true); + } + SimpleTest.executeSoon(func); +} -SpecialPowers.addCategoryEntry("JavaScript-navigator-property", "randomname2", "@mozilla.org/embedcomp/prompt-service;1", - false, true); +function removeCategoryEntries(func) { + for (var categoryEntry of categoryEntries) { + SpecialPowers.deleteCategoryEntry(categoryEntry.category, categoryEntry.entry, false); + } + SimpleTest.executeSoon(func); +} -SimpleTest.executeSoon(function () { +function checkNamesPresent() { ok(window.randomname, "window.randomname should return an object"); is(typeof(window.navigator.randomname1), 'object', "navigator.randomname1 should return an object"); is(typeof(window.navigator.randomname2), 'object', "navigator.randomname1 should return an object"); - SimpleTest.finish(); +} + +function checkNamesAbsent() { + ok(!window.randomname, "window.randomname should return undefined"); + is(typeof(window.navigator.randomname1), 'undefined', "navigator.randomname1 should return undefined"); + is(typeof(window.navigator.randomname2), 'undefined', "navigator.randomname1 should return undefined"); +} + +// Ensure the initial state +checkNamesAbsent(); + +addCategoryEntries(function test1() { + ok(window.randomname, "window.randomname should return an object"); + is(typeof(window.navigator.randomname1), 'object', "navigator.randomname1 should return an object"); + is(typeof(window.navigator.randomname2), 'object', "navigator.randomname1 should return an object"); + + delete window.randomname; + delete window.navigator.randomname1; + delete window.navigator.randomname2; + + // The delete opertor should have no effect as long as the category entry is registered. + checkNamesPresent(); + + removeCategoryEntries(test2); }); +function test2() { + // The object should be cached on the global/navigator object once accessed. + checkNamesPresent(); + + delete window.randomname; + delete window.navigator.randomname1; + delete window.navigator.randomname2; + + // Now the delete opertor should have the effect. + checkNamesAbsent(); + + addCategoryEntries(function() { + removeCategoryEntries(test3); + }); +} + +function test3() { + // The object should not be cached until the first access. + checkNamesAbsent(); + + SimpleTest.finish(); +} diff --git a/testing/specialpowers/content/specialpowersAPI.js b/testing/specialpowers/content/specialpowersAPI.js index 2ecc3466922b..bd9cedfbbaff 100644 --- a/testing/specialpowers/content/specialpowersAPI.js +++ b/testing/specialpowers/content/specialpowersAPI.js @@ -1291,6 +1291,12 @@ SpecialPowersAPI.prototype = { addCategoryEntry(category, entry, value, persists, replace); }, + deleteCategoryEntry: function(category, entry, persists) { + Components.classes["@mozilla.org/categorymanager;1"]. + getService(Components.interfaces.nsICategoryManager). + deleteCategoryEntry(category, entry, persists); + }, + copyString: function(str, doc) { Components.classes["@mozilla.org/widget/clipboardhelper;1"]. getService(Components.interfaces.nsIClipboardHelper). From 5188bb1a3e9b1f32e9c8113ef3737f4a8120a605 Mon Sep 17 00:00:00 2001 From: Masatoshi Kimura Date: Thu, 22 Aug 2013 22:44:19 +0900 Subject: [PATCH 43/54] Bug 906432 - Use getOwnPropertyNames() in test_interfaces.html. r=smaug --- dom/tests/mochitest/bugs/test_bug597809.html | 3 + .../mochitest/general/file_interfaces.xml | 7 +- .../mochitest/general/test_interfaces.html | 295 +++++++++++++----- 3 files changed, 217 insertions(+), 88 deletions(-) diff --git a/dom/tests/mochitest/bugs/test_bug597809.html b/dom/tests/mochitest/bugs/test_bug597809.html index ac2daa4211c4..459457481c86 100644 --- a/dom/tests/mochitest/bugs/test_bug597809.html +++ b/dom/tests/mochitest/bugs/test_bug597809.html @@ -23,6 +23,9 @@ SpecialPowers.addCategoryEntry("JavaScript-global-property", "testSNSM", "@mozil SimpleTest.executeSoon(function () { ok(window.testSNSM, "testSNSM should return an object"); + // The category entry must be removed before finishing the test, + // otherwise it will affect all following tests in the test suite. + SpecialPowers.deleteCategoryEntry("JavaScript-global-property", "testSNSM", false); SimpleTest.finish(); }); diff --git a/dom/tests/mochitest/general/file_interfaces.xml b/dom/tests/mochitest/general/file_interfaces.xml index 65bb7c5cf593..527546c42415 100644 --- a/dom/tests/mochitest/general/file_interfaces.xml +++ b/dom/tests/mochitest/general/file_interfaces.xml @@ -7,11 +7,12 @@ var win = XPCNativeWrapper.unwrap(window); var SpecialPowers = win.SpecialPowers; var is = win.is; + var todo_is = win.todo_is; var ok = win.ok; - var interfaceNamesInGlobalScope = win.interfaceNamesInGlobalScope; - var interfaceNamesInXBLScope = win.interfaceNamesInXBLScope; + var excludedNames = win.excludedNames; + var createInterfaceMap = win.createInterfaceMap; eval(win.runTest.toString()); - runTest(window, true); + runTest(true); win.SimpleTest.finish(); diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html index 69fc3400f2e4..128bb7c9808c 100644 --- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -23,26 +23,88 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=766694 // This is a list of all interfaces that are exposed to every webpage. Please only // add things to this list with great care. +// IMPORTANT: Do not change this list without review from a JavaScript Engine peer! +var ecmaGlobals = + [ + "Array", + "ArrayBuffer", + "Boolean", + "DataView", + "Date", + "Error", + "EvalError", + "Float32Array", + "Float64Array", + "Function", + "Infinity", + "Int16Array", + "Int32Array", + "Int8Array", + "InternalError", + "Iterator", + "JSON", + "Map", + "Math", + "NaN", + "Number", + "Object", + {name: "ParallelArray", nightly: true}, + "Proxy", + "RangeError", + "ReferenceError", + "RegExp", + "Set", + "StopIteration", + "String", + "SyntaxError", + "TypeError", + "Uint16Array", + "Uint32Array", + "Uint8Array", + "Uint8ClampedArray", + "URIError", + "WeakMap", + ]; +// IMPORTANT: Do not change this list without review from a JavaScript Engine peer! + // IMPORTANT: Do not change this list without review from a DOM peer! var interfaceNamesInGlobalScope = [ + "AnalyserNode", "AnimationEvent", - "Array", + "ArchiveRequest", "AsyncScrollEventDetail", "Attr", + "Audio", + "AudioBuffer", + "AudioContext", + "AudioBufferSourceNode", + "AudioDestinationNode", + "AudioListener", + "AudioNode", + "AudioParam", + "AudioProcessingEvent", + "AudioStreamTrack", "BarProp", "BatteryManager", "BeforeUnloadEvent", + "BiquadFilterNode", "Blob", "BlobEvent", + {name: "BoxObject", xbl: true}, + {name: "BrowserFeedWriter", android: false}, "CameraCapabilities", "CameraControl", "CameraManager", "CanvasGradient", "CanvasPattern", "CanvasRenderingContext2D", + "CaretPosition", "CDATASection", + "ChannelMergerNode", + "ChannelSplitterNode", "CharacterData", + {name: "ChromeWindow", xbl: true}, "ClientRect", "ClientRectList", "ClipboardEvent", @@ -51,17 +113,21 @@ var interfaceNamesInGlobalScope = "Comment", "CompositionEvent", "Controllers", - "CRMFObject", + "ConvolverNode", + {name: "CRMFObject", android: false}, "Crypto", + "CSS", "CSS2Properties", "CSSCharsetRule", "CSSConditionRule", "CSSFontFaceRule", - "CSSFontFeatureValuesRule", + {name: "CSSFontFeatureValuesRule", release: false}, "CSSGroupingRule", + "CSSGroupRuleRuleList", "CSSImportRule", "CSSMediaRule", "CSSMozDocumentRule", + "CSSNameSpaceRule", "CSSPageRule", "CSSPrimitiveValue", "CSSRule", @@ -76,6 +142,7 @@ var interfaceNamesInGlobalScope = "DataChannel", "DataContainerEvent", "DataTransfer", + "DelayNode", "DesktopNotification", "DesktopNotificationCenter", "DeviceAcceleration", @@ -86,14 +153,16 @@ var interfaceNamesInGlobalScope = "DeviceRotationRate", "DeviceStorage", "DeviceStorageChangeEvent", - "DeviceStorageCursor", "Document", "DocumentFragment", "DocumentType", + {name: "DOMConstructor", xbl: true}, "DOMCursor", "DOMError", "DOMException", "DOMImplementation", + "DOMMMIError", + "DOMParser", "DOMRequest", "DOMSettableTokenList", "DOMStringList", @@ -101,9 +170,9 @@ var interfaceNamesInGlobalScope = "DOMTokenList", "DOMTransactionEvent", "DragEvent", + "DynamicsCompressorNode", "Element", "ElementReplaceEvent", - "ElementTimeControl", "Event", "EventListenerInfo", "EventSource", @@ -115,10 +184,11 @@ var interfaceNamesInGlobalScope = "FileRequest", "FocusEvent", "FormData", - "Gamepad", - "GamepadAxisMoveEvent", - "GamepadButtonEvent", - "GamepadEvent", + "GainNode", + {name: "Gamepad", android: false}, + {name: "GamepadAxisMoveEvent", android: false}, + {name: "GamepadButtonEvent", android: false}, + {name: "GamepadEvent", android: false}, "HashChangeEvent", "History", "HTMLAnchorElement", @@ -131,7 +201,7 @@ var interfaceNamesInGlobalScope = "HTMLButtonElement", "HTMLCanvasElement", "HTMLCollection", - "HTMLCommandElement", + "HTMLDataElement", "HTMLDataListElement", "HTMLDirectoryElement", "HTMLDivElement", @@ -177,6 +247,7 @@ var interfaceNamesInGlobalScope = "HTMLScriptElement", "HTMLSelectElement", "HTMLSourceElement", + "HTMLSpanElement", "HTMLStyleElement", "HTMLTableCaptionElement", "HTMLTableCellElement", @@ -184,7 +255,9 @@ var interfaceNamesInGlobalScope = "HTMLTableElement", "HTMLTableRowElement", "HTMLTableSectionElement", + "HTMLTemplateElement", "HTMLTextAreaElement", + "HTMLTimeElement", "HTMLTitleElement", "HTMLUListElement", "HTMLUnknownElement", @@ -193,6 +266,7 @@ var interfaceNamesInGlobalScope = "IDBCursorWithValue", "IDBDatabase", "IDBFactory", + "IDBFileHandle", "IDBIndex", "IDBKeyRange", "IDBObjectStore", @@ -200,18 +274,25 @@ var interfaceNamesInGlobalScope = "IDBRequest", "IDBTransaction", "IDBVersionChangeEvent", + "Image", "ImageData", - "JSON", + {name: "InstallTrigger", xbl: false}, "KeyEvent", + "KeyboardEvent", "LoadStatus", "LocalMediaStream", "Location", "LockedFile", - "LSProgressEvent", + "MediaElementAudioSourceNode", "MediaError", "MediaList", "MediaQueryList", + "MediaRecorder", "MediaStream", + "MediaStreamAudioDestinationNode", + "MediaStreamAudioSourceNode", + "MediaStreamEvent", + "MediaStreamTrack", "MessageEvent", "MimeType", "MimeTypeArray", @@ -219,31 +300,26 @@ var interfaceNamesInGlobalScope = "MouseEvent", "MouseScrollEvent", "MozApplicationEvent", - "MozBlobBuilder", "MozCanvasPrintState", "MozConnection", + "mozContact", "MozContactChangeEvent", "MozCSSKeyframeRule", "MozCSSKeyframesRule", "MozMmsEvent", "MozMmsMessage", - "MozMobileCellInfo", - "MozMobileConnectionInfo", "MozMobileMessageManager", "MozMobileMessageThread", - "MozMobileNetworkInfo", "MozNamedAttrMap", - "MozNetworkStats", - "MozNetworkStatsData", - "MozNetworkStatsManager", "MozPowerManager", + "mozRTCIceCandidate", + "mozRTCPeerConnection", + "mozRTCSessionDescription", "MozSettingsEvent", "MozSmsEvent", "MozSmsFilter", "MozSmsMessage", "MozSmsSegmentInfo", - "MozTimeManager", - "MozTouchEvent", "MozWakeLock", "MutationEvent", "MutationObserver", @@ -253,47 +329,54 @@ var interfaceNamesInGlobalScope = "NodeFilter", "NodeIterator", "NodeList", - "NodeSelector", "NotifyAudioAvailableEvent", + "Notification", "NotifyPaintEvent", - "NSRGBAColor", + "OfflineAudioCompletionEvent", + "OfflineAudioContext", "OfflineResourceList", "OpenWindowEventDetail", + "Option", + "OscillatorNode", "PageTransitionEvent", "PaintRequest", "PaintRequestList", - "PaymentRequestInfo", + "PannerNode", "Performance", "PerformanceNavigation", "PerformanceTiming", + "PeriodicWave", + "PhoneNumberService", "Plugin", "PluginArray", "PopStateEvent", "PopupBlockedEvent", "ProcessingInstruction", "ProgressEvent", + {name: "Promise", release: false}, + {name: "PromiseResolver", release: false}, "PropertyNodeList", "Range", "RecordErrorEvent", "Rect", "RGBColor", - "RTCIceCandidate", - "RTCPeerConnection", - "RTCSessionDescription", + "RTCDataChannelEvent", + "RTCPeerConnectionIceEvent", "Screen", + "ScriptProcessorNode", "ScrollAreaEvent", "Selection", "SettingsLock", "SettingsManager", "SimpleGestureEvent", + {name: "SimpleTest", xbl: false}, "SmartCardEvent", "SpeechRecognitionError", "SpeechRecognitionEvent", "SpeechSynthesisEvent", + {name: "SpecialPowers", xbl: false}, "Storage", "StorageEvent", - "StorageItem", - "StorageObsolete", "StyleRuleChangeEvent", "StyleSheet", "StyleSheetApplicableStateChangeEvent", @@ -310,8 +393,6 @@ var interfaceNamesInGlobalScope = "SVGAnimatedLengthList", "SVGAnimatedNumber", "SVGAnimatedNumberList", - "SVGAnimatedPathData", - "SVGAnimatedPoints", "SVGAnimatedPreserveAspectRatio", "SVGAnimatedRect", "SVGAnimatedString", @@ -328,7 +409,6 @@ var interfaceNamesInGlobalScope = "SVGDocument", "SVGElement", "SVGEllipseElement", - "SVGEvent", "SVGFEBlendElement", "SVGFEColorMatrixElement", "SVGFEComponentTransferElement", @@ -354,22 +434,20 @@ var interfaceNamesInGlobalScope = "SVGFETileElement", "SVGFETurbulenceElement", "SVGFilterElement", - "SVGFilterPrimitiveStandardAttributes", - "SVGFitToViewBox", "SVGForeignObjectElement", "SVGGElement", "SVGGradientElement", + "SVGGraphicsElement", "SVGImageElement", "SVGLength", "SVGLengthList", "SVGLinearGradientElement", "SVGLineElement", - "SVGLocatable", "SVGMarkerElement", "SVGMaskElement", "SVGMatrix", "SVGMetadataElement", - "SVGMpathElement", + "SVGMPathElement", "SVGNumber", "SVGNumberList", "SVGPathElement", @@ -407,31 +485,26 @@ var interfaceNamesInGlobalScope = "SVGSetElement", "SVGStopElement", "SVGStringList", - "SVGStylable", "SVGStyleElement", "SVGSVGElement", "SVGSwitchElement", "SVGSymbolElement", - "SVGTests", "SVGTextContentElement", "SVGTextElement", "SVGTextPathElement", "SVGTextPositioningElement", "SVGTitleElement", "SVGTransform", - "SVGTransformable", "SVGTransformList", "SVGTSpanElement", "SVGUnitTypes", - "SVGURIReference", "SVGUseElement", "SVGViewElement", - "SVGViewSpec", "SVGZoomAndPan", "SVGZoomEvent", - "TCPSocket", - "TCPServerSocket", "Text", + "TextDecoder", + "TextEncoder", "TextMetrics", "TimeEvent", "TimeRanges", @@ -439,79 +512,131 @@ var interfaceNamesInGlobalScope = "TouchEvent", "TouchList", "TransitionEvent", + {name: "TreeColumn", xbl: true}, + {name: "TreeColumns", xbl: true}, + {name: "TreeContentView", xbl: true}, + {name: "TreeSelection", xbl: true}, "TreeWalker", "UIEvent", "UndoManager", "URL", "UserDataHandler", "UserProximityEvent", - "USSDReceivedEvent", "ValidityState", + "VideoStreamTrack", + "WaveShaperNode", + "WebGLActiveInfo", + "WebGLBuffer", + "WebGLFramebuffer", + "WebGLProgram", + "WebGLRenderbuffer", "WebGLRenderingContext", + "WebGLShader", + "WebGLShaderPrecisionFormat", + "WebGLTexture", + "WebGLUniformLocation", + "WebGLVertexArray", "WebSocket", "WheelEvent", "Window", "WindowUtils", "XMLDocument", "XMLHttpRequest", - "XMLHttpRequestEventTarget", "XMLHttpRequestUpload", + "XMLSerializer", + "XMLStylesheetProcessingInstruction", "XPathEvaluator", "XPathExpression", "XPathNamespace", "XPathNSResolver", "XPathResult", "XSLTProcessor", - "XULElement", + {name: "XULButtonElement", xbl: true}, + {name: "XULCheckboxElement", xbl: true}, + {name: "XULCommandDispatcher", xbl: true}, + {name: "XULCommandEvent", xbl: true}, + {name: "XULControlElement", xbl: true}, + {name: "XULControllers", xbl: true}, + {name: "XULDocument", xbl: true}, + {name: "XULElement", win: true, xbl: true}, + {name: "XULLabeledControlElement", xbl: true}, + {name: "XULPopupElement", xbl: true}, + {name: "XULTemplateBuilder", xbl: true}, + {name: "XULTreeBuilder", xbl: true}, ] // IMPORTANT: Do not change this list without review from a DOM peer! -// If your interface is named nsIDOMSomeInterface and you don't mean to expose -// it to every webpage, simply change its name to nsISomeInterface to fix this problem. - - var interfaceNamesInXBLScope = [ - "BoxObject", - "ChromeWindow", - "DOMConstructor", - "TreeColumn", - "TreeColumns", - "TreeContentView", - "TreeSelection", - "XULButtonElement", - "XULCheckboxElement", - "XULCommandDispatcher", - "XULCommandEvent", - "XULControlElement", - "XULControllers", - "XULDocument", - "XULLabeledControlElement", - "XULPopupElement", - "XULTemplateBuilder", - "XULTreeBuilder", + var excludedNames = [ + "TraceMallocDisable", // debug builds only + "TraceMallocEnable", // debug builds only + "TraceMallocOpenLogFile", // debug builds only + "TraceMallocChangeLogFD", // debug builds only + "TraceMallocCloseLogFD", // debug builds only + "TraceMallocLogTimestamp", // debug builds only + "TraceMallocDumpAllocations", // debug builds only ]; -function runTest(win, isXBLScope) { - for (var i in SpecialPowers.Components.interfaces) { - var s = i.toString(); - var name = null; - if (s.indexOf("nsIDOM") == 0) { - name = s.substring("nsIDOM".length); - } else if (s.indexOf("nsI") == 0) { - name = s.substring("nsI".length); - } - if (name && (name in win)) { - ok(interfaceNamesInGlobalScope.indexOf(name) >= 0 || - (isXBLScope && interfaceNamesInXBLScope.indexOf(name) >= 0), - "If this is failing: DANGER, are you sure you want to expose the new interface " + name + " to all webpages as a property on the window?"); +function createInterfaceMap(isXBLScope) { + var version = SpecialPowers.Cc["@mozilla.org/xre/app-info;1"].getService(SpecialPowers.Ci.nsIXULAppInfo).version; + var isNightly = version.endsWith("a1"); + var isRelease = !version.contains("a"); + var isAndroid = navigator.userAgent.indexOf("Android") >= 0; + var isWin = navigator.userAgent.indexOf("Windows") >= 0; + var interfaceMap = {}; + for (var entry of ecmaGlobals) { + if (typeof(entry) === "string") { + // Standard ECMAScript global objects are not defined on the XBL scope. + interfaceMap[entry] = !isXBLScope; + } else if (entry.nightly === isNightly) { + interfaceMap[entry.name] = !isXBLScope; + } else { + interfaceMap[entry.name] = false; } } - for (var name of interfaceNamesInXBLScope) { - is(name in win, isXBLScope, - name + " should" + (isXBLScope ? "" : " NOT") + " be present in the global scope"); + for (var entry of interfaceNamesInGlobalScope) { + if (typeof(entry) === "string") { + interfaceMap[entry] = true; + } else if (entry.xbl === isXBLScope || + entry.win === isWin || + entry.android === isAndroid || + entry.release === isRelease) { + interfaceMap[entry.name] = true; + } else { + interfaceMap[entry.name] = false; + } + } + return interfaceMap; +} + +function runTest(isXBLScope) { + var interfaceMap = createInterfaceMap(isXBLScope); + for (var name of Object.getOwnPropertyNames(window)) { + // An interfae name should start with an upper case character. + if (!/^(moz)?[A-Z]/.test(name) || + excludedNames.indexOf(name) >= 0) { + continue; + } + ok(interfaceMap[name], + "If this is failing: DANGER, are you sure you want to expose the new interface " + name + " to all webpages as a property on the window?"); + delete interfaceMap[name]; + } + for (var name of Object.keys(interfaceMap)) { + ok(name in window === interfaceMap[name], + name + " should " + (interfaceMap[name] ? "" : " NOT") + " be defined on the " + (isXBLScope ? "XBL" : "global") +" scope"); + if (!interfaceMap[name]) { + delete interfaceMap[name]; + } + } + if (isXBLScope) { + todo_is(Object.keys(interfaceMap).length, 0, + "The following interface(s) are not enumerated: " + Object.keys(interfaceMap).join(", ")); + } else { + is(Object.keys(interfaceMap).length, 0, + "The following interface(s) are not enumerated: " + Object.keys(interfaceMap).join(", ")); } } -runTest(window, false); +runTest(false); SimpleTest.waitForExplicitFinish(); From 6b18d580a4c51cb179d4b3dceb9eea43adb7695d Mon Sep 17 00:00:00 2001 From: Masatoshi Kimura Date: Thu, 22 Aug 2013 22:44:19 +0900 Subject: [PATCH 44/54] Bug 907968 - Remove a useless (even harmful) log. r=gps --- python/mozbuild/mozbuild/backend/configenvironment.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/python/mozbuild/mozbuild/backend/configenvironment.py b/python/mozbuild/mozbuild/backend/configenvironment.py index 62a664e8456d..0035b7169f79 100644 --- a/python/mozbuild/mozbuild/backend/configenvironment.py +++ b/python/mozbuild/mozbuild/backend/configenvironment.py @@ -141,10 +141,6 @@ class ConfigEnvironment(object): try: v = v.decode('utf-8') except UnicodeDecodeError: - log(self._log, logging.INFO, 'lossy_encoding', - {'variable': k}, - 'Lossy Unicode encoding for {variable}. See bug 844509.') - v = v.decode('utf-8', 'replace') self.substs_unicode[k] = v From 31b30305a35b3c3dcd756a4e02717ec83614eab5 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Thu, 22 Aug 2013 09:46:26 -0400 Subject: [PATCH 45/54] Bug 907548. URL.prototype should be undefined, since we don't implement the URL API. r=khuey --- dom/bindings/Bindings.conf | 8 +++++--- dom/bindings/Configuration.py | 7 +++++-- dom/bindings/test/Makefile.in | 1 + dom/bindings/test/test_bug907548.html | 29 +++++++++++++++++++++++++++ 4 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 dom/bindings/test/test_bug907548.html diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 9870cf4292ad..4f2d953ab96c 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -20,9 +20,10 @@ # interfaces. # * headerFile - The file in which the nativeType is declared (defaults # to an educated guess). -# * concrete - Indicates whether there exist objects with this interface as -# their primary interface. Always False for callback interfaces. -# defaults to True otherwise. +# * concrete - Indicates whether there exist JS objects with this interface as +# their primary interface (and hence whose prototype is this +# interface's prototype object). Always False for callback +# interfaces. Defaults to True otherwise. # * workers - Indicates whether the descriptor is intended to be used for # worker threads (defaults to false). # * customTrace - The native class will use a custom trace hook (defaults to @@ -1236,6 +1237,7 @@ DOMInterfaces = { { 'implicitJSContext': [ 'createObjectURL', 'revokeObjectURL' ], 'workers': True, + 'concrete': False, }], 'WebGLActiveInfo': { diff --git a/dom/bindings/Configuration.py b/dom/bindings/Configuration.py index c63ce0fbf8ba..7359b8364f89 100644 --- a/dom/bindings/Configuration.py +++ b/dom/bindings/Configuration.py @@ -480,11 +480,14 @@ class Descriptor(DescriptorProvider): def needsHeaderInclude(self): """ An interface doesn't need a header file if it is not concrete, - not pref-controlled, and has only consts. + not pref-controlled, has no prototype object, and has no + static methods or attributes. """ return (self.interface.isExternal() or self.concrete or self.interface.getExtendedAttribute("PrefControlled") or - self.interface.hasInterfacePrototypeObject()) + self.interface.hasInterfacePrototypeObject() or + any((m.isAttr() or m.isMethod()) and m.isStatic() for m + in self.interface.members)) def wantsQI(self): # If it was specified explicitly use that. diff --git a/dom/bindings/test/Makefile.in b/dom/bindings/test/Makefile.in index 0d03c7d04502..fd386e68faa9 100644 --- a/dom/bindings/test/Makefile.in +++ b/dom/bindings/test/Makefile.in @@ -76,6 +76,7 @@ MOCHITEST_FILES := \ test_ByteString.html \ test_exception_messages.html \ test_bug707564.html \ + test_bug907548.html \ $(NULL) MOCHITEST_CHROME_FILES = \ diff --git a/dom/bindings/test/test_bug907548.html b/dom/bindings/test/test_bug907548.html new file mode 100644 index 000000000000..2d2544599309 --- /dev/null +++ b/dom/bindings/test/test_bug907548.html @@ -0,0 +1,29 @@ + + + + + + Test for Bug 907548 + + + + + +Mozilla Bug 907548 +

+ +
+
+ + From 4fa095c3f45eb422b118adc99839e92f92e4315e Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Thu, 22 Aug 2013 09:46:27 -0400 Subject: [PATCH 46/54] Bug 907892. Disallow setting document.domain in sandboxed iframes. r=smaug --- content/base/src/nsContentUtils.cpp | 3 +- content/base/src/nsSandboxFlags.h | 5 ++ content/base/test/Makefile.in | 2 + content/base/test/file_bug907892.html | 12 +++++ content/base/test/test_bug907892.html | 49 ++++++++++++++++++++ content/html/document/src/nsHTMLDocument.cpp | 6 +++ 6 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 content/base/test/file_bug907892.html create mode 100644 content/base/test/test_bug907892.html diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index c208ccd6f930..10581e5a6f61 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -927,7 +927,8 @@ nsContentUtils::ParseSandboxAttributeToFlags(const nsAString& aSandboxAttrValue) SANDBOXED_FORMS | SANDBOXED_SCRIPTS | SANDBOXED_AUTOMATIC_FEATURES | - SANDBOXED_POINTER_LOCK; + SANDBOXED_POINTER_LOCK | + SANDBOXED_DOMAIN; if (!aSandboxAttrValue.IsEmpty()) { // The separator optional flag is used because the HTML5 spec says any diff --git a/content/base/src/nsSandboxFlags.h b/content/base/src/nsSandboxFlags.h index 669d84a9d5c3..84acafa4b1c8 100644 --- a/content/base/src/nsSandboxFlags.h +++ b/content/base/src/nsSandboxFlags.h @@ -60,4 +60,9 @@ const unsigned long SANDBOXED_AUTOMATIC_FEATURES = 0x40; * This flag blocks the document from acquiring pointerlock. */ const unsigned long SANDBOXED_POINTER_LOCK = 0x80; + +/** + * This flag blocks the document from changing document.domain. + */ +const unsigned long SANDBOXED_DOMAIN = 0x100; #endif diff --git a/content/base/test/Makefile.in b/content/base/test/Makefile.in index 9c36b5af9c49..c65f12c51872 100644 --- a/content/base/test/Makefile.in +++ b/content/base/test/Makefile.in @@ -664,6 +664,8 @@ MOCHITEST_FILES_C= \ file_CSP_bug802872.html^headers^ \ file_CSP_bug802872.js \ file_CSP_bug802872.sjs \ + test_bug907892.html \ + file_bug907892.html \ $(NULL) # OOP tests don't work on Windows (bug 763081) or native-fennec diff --git a/content/base/test/file_bug907892.html b/content/base/test/file_bug907892.html new file mode 100644 index 000000000000..bbd417a16733 --- /dev/null +++ b/content/base/test/file_bug907892.html @@ -0,0 +1,12 @@ + + diff --git a/content/base/test/test_bug907892.html b/content/base/test/test_bug907892.html new file mode 100644 index 000000000000..5aecc7e372fa --- /dev/null +++ b/content/base/test/test_bug907892.html @@ -0,0 +1,49 @@ + + + + + + Test for Bug 907892 + + + + + +Mozilla Bug 907892 +

+ +
+
+ + diff --git a/content/html/document/src/nsHTMLDocument.cpp b/content/html/document/src/nsHTMLDocument.cpp index 8100588afea0..bc15818cac31 100644 --- a/content/html/document/src/nsHTMLDocument.cpp +++ b/content/html/document/src/nsHTMLDocument.cpp @@ -1006,6 +1006,12 @@ nsHTMLDocument::SetDomain(const nsAString& aDomain) void nsHTMLDocument::SetDomain(const nsAString& aDomain, ErrorResult& rv) { + if (mSandboxFlags & SANDBOXED_DOMAIN) { + // We're sandboxed; disallow setting domain + rv.Throw(NS_ERROR_DOM_SECURITY_ERR); + return; + } + if (aDomain.IsEmpty()) { rv.Throw(NS_ERROR_DOM_BAD_DOCUMENT_DOMAIN); return; From 534c27f2a6569acd6c1f9aa17f2bf10a772f6b26 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Thu, 22 Aug 2013 09:46:27 -0400 Subject: [PATCH 47/54] Bug 907727. Make sure to properly forward gets to the expando object for DOM proxies. r=peterv --- dom/bindings/Codegen.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index b32331ce6dd0..ab7fff2c2c8d 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -7518,7 +7518,9 @@ if (expando) { } if (hasProp) { - return JS_GetPropertyById(cx, expando, id, vp); + // Forward the get to the expando object, but our receiver is whatever our + // receiver is. + return JS_ForwardGetPropertyTo(cx, expando, id, receiver, vp); } }""" From f070b832888e920bcc630172a4cc1a3ce0801d4e Mon Sep 17 00:00:00 2001 From: Mina Almasry Date: Thu, 22 Aug 2013 10:16:06 -0400 Subject: [PATCH 48/54] Bug 728629 - canvas2D fillText & strokeText do not throw exceptions when non-finite doubles are passed for the position. r=vlad --- content/canvas/test/Makefile.in | 1 + .../canvas/test/test_strokeText_throw.html | 68 +++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 content/canvas/test/test_strokeText_throw.html diff --git a/content/canvas/test/Makefile.in b/content/canvas/test/Makefile.in index 3df57447e1fb..63c825bed0a0 100644 --- a/content/canvas/test/Makefile.in +++ b/content/canvas/test/Makefile.in @@ -87,6 +87,7 @@ MOCHITEST_FILES = \ test_mozDashOffset.html \ file_drawImage_document_domain.html \ test_windingRuleUndefined.html \ + test_strokeText_throw.html \ $(NULL) # SkiaGL on Android/Gonk does not implement these composite ops yet diff --git a/content/canvas/test/test_strokeText_throw.html b/content/canvas/test/test_strokeText_throw.html new file mode 100644 index 000000000000..59a6975eaecc --- /dev/null +++ b/content/canvas/test/test_strokeText_throw.html @@ -0,0 +1,68 @@ + + + + + + Test for Bug 728629 + + + + + +Mozilla Bug 728629 +

+ +
+
+ + From cf5bf2af0000be00f388330e96275a946742033c Mon Sep 17 00:00:00 2001 From: Andy Wingo Date: Thu, 22 Aug 2013 10:16:07 -0400 Subject: [PATCH 49/54] Bug 904701 - Implement prototype madness for ES6 generators. r=bhackett, r=jorendorff Add a Generator constructor, like the Function constructor. Define the GeneratorFunctionPrototype, GeneratorFunction, and GeneratorObjectPrototype meta-objects. When cloning or creating a star generator, give it GeneratorFunctionPrototype as its prototype. Each star generator function has a ".prototype" property, which has GeneratorObjectPrototype as its prototype. That prototype does not have a ".constructor" link. If Function.prototype.toSource is called on a non-function, chain up to Object.prototype.toSource instead. Prototypes of generator objects are no longer made with GeneratorObject::class_. This allows us to elide some "null" checks from bug 352885. Instead calling a generator on a method now signals a typeerror. Make the "send" generator method a simple alias to "next". --- js/src/builtin/Object.cpp | 4 +- js/src/builtin/Object.h | 6 +- js/src/frontend/BytecodeCompiler.cpp | 24 ++- js/src/frontend/BytecodeCompiler.h | 3 + js/src/frontend/Parser.cpp | 18 +- js/src/frontend/Parser.h | 3 +- js/src/jsapi.h | 2 +- js/src/jsfun.cpp | 177 ++++++++++----- js/src/jsfun.h | 10 + js/src/jsiter.cpp | 203 +++++++++++------- js/src/jsprototypes.h | 1 + js/src/jsworkers.cpp | 6 +- js/src/tests/ecma_6/Generators/runtime.js | 150 +++++++++++++ .../js1_7/extensions/regress-352885-02.js | 17 +- js/src/vm/GlobalObject.h | 22 +- 15 files changed, 490 insertions(+), 156 deletions(-) create mode 100644 js/src/tests/ecma_6/Generators/runtime.js diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index 51fc64aca663..970f40eac885 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -87,8 +87,8 @@ obj_propertyIsEnumerable(JSContext *cx, unsigned argc, Value *vp) } #if JS_HAS_TOSOURCE -static bool -obj_toSource(JSContext *cx, unsigned argc, Value *vp) +bool +js::obj_toSource(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); JS_CHECK_RECURSION(cx, return false); diff --git a/js/src/builtin/Object.h b/js/src/builtin/Object.h index 79e784f78ced..892dce5c2199 100644 --- a/js/src/builtin/Object.h +++ b/js/src/builtin/Object.h @@ -14,10 +14,14 @@ namespace js { extern const JSFunctionSpec object_methods[]; extern const JSFunctionSpec object_static_methods[]; -/* Object constructor native. Exposed only so the JIT can know its address. */ +// Object constructor native. Exposed only so the JIT can know its address. extern bool obj_construct(JSContext *cx, unsigned argc, js::Value *vp); +// Object.prototype.toSource. Exposed so that Function.prototype.toSource can chain up. +extern bool +obj_toSource(JSContext *cx, unsigned argc, js::Value *vp); + } /* namespace js */ #endif /* builtin_Object_h */ diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp index 27ffbb044061..3a199d261f6c 100644 --- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -452,9 +452,10 @@ frontend::CompileLazyFunction(JSContext *cx, LazyScript *lazy, const jschar *cha // Compile a JS function body, which might appear as the value of an event // handler attribute in an HTML tag, or in a Function() constructor. -bool -frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileOptions options, - const AutoNameVector &formals, const jschar *chars, size_t length) +static bool +CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileOptions options, + const AutoNameVector &formals, const jschar *chars, size_t length, + GeneratorKind generatorKind) { #if JS_TRACE_LOGGING js::AutoTraceLog logger(js::TraceLogging::defaultLogger(), @@ -521,7 +522,7 @@ frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileO ParseNode *fn; while (true) { Directives newDirectives = directives; - fn = parser.standaloneFunctionBody(fun, formals, NotGenerator, directives, &newDirectives); + fn = parser.standaloneFunctionBody(fun, formals, generatorKind, directives, &newDirectives); if (fn) break; @@ -586,3 +587,18 @@ frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileO return true; } + +bool +frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileOptions options, + const AutoNameVector &formals, const jschar *chars, size_t length) +{ + return CompileFunctionBody(cx, fun, options, formals, chars, length, NotGenerator); +} + +bool +frontend::CompileStarGeneratorBody(JSContext *cx, MutableHandleFunction fun, + CompileOptions options, const AutoNameVector &formals, + const jschar *chars, size_t length) +{ + return CompileFunctionBody(cx, fun, options, formals, chars, length, StarGenerator); +} diff --git a/js/src/frontend/BytecodeCompiler.h b/js/src/frontend/BytecodeCompiler.h index 12721bcfec09..109e8031cecc 100644 --- a/js/src/frontend/BytecodeCompiler.h +++ b/js/src/frontend/BytecodeCompiler.h @@ -33,6 +33,9 @@ CompileLazyFunction(JSContext *cx, LazyScript *lazy, const jschar *chars, size_t bool CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileOptions options, const AutoNameVector &formals, const jschar *chars, size_t length); +bool +CompileStarGeneratorBody(JSContext *cx, MutableHandleFunction fun, CompileOptions options, + const AutoNameVector &formals, const jschar *chars, size_t length); /* * This should be called while still on the main thread if compilation will diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 856249dbcffd..a2fa03b8fc95 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -1243,7 +1243,7 @@ struct BindData template JSFunction * Parser::newFunction(GenericParseContext *pc, HandleAtom atom, - FunctionSyntaxKind kind) + FunctionSyntaxKind kind, JSObject *proto) { JS_ASSERT_IF(kind == Statement, atom != NULL); @@ -1266,8 +1266,8 @@ Parser::newFunction(GenericParseContext *pc, HandleAtom atom, : (kind == Arrow) ? JSFunction::INTERPRETED_LAMBDA_ARROW : JSFunction::INTERPRETED; - fun = NewFunction(context, NullPtr(), NULL, 0, flags, parent, atom, - JSFunction::FinalizeKind, MaybeSingletonObject); + fun = NewFunctionWithProto(context, NullPtr(), NULL, 0, flags, parent, atom, proto, + JSFunction::FinalizeKind, MaybeSingletonObject); if (!fun) return NULL; if (options().selfHostingMode) @@ -1993,7 +1993,17 @@ Parser::functionDef(HandlePropertyName funName, const TokenStream: if (bodyProcessed) return pn; - RootedFunction fun(context, newFunction(pc, funName, kind)); + RootedObject proto(context); + if (generatorKind == StarGenerator) { + // If we are off the main thread, the generator meta-objects have + // already been created by js::StartOffThreadParseScript, so cx will not + // be necessary. + JSContext *cx = context->maybeJSContext(); + proto = context->global()->getOrCreateStarGeneratorFunctionPrototype(cx); + if (!proto) + return null(); + } + RootedFunction fun(context, newFunction(pc, funName, kind, proto)); if (!fun) return null(); diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 9b933f7377b5..3421ec5a2153 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -407,7 +407,8 @@ class Parser : private AutoGCRooter, public StrictModeGetter * Create a new function object given parse context (pc) and a name (which * is optional if this is a function expression). */ - JSFunction *newFunction(GenericParseContext *pc, HandleAtom atom, FunctionSyntaxKind kind); + JSFunction *newFunction(GenericParseContext *pc, HandleAtom atom, FunctionSyntaxKind kind, + JSObject *proto = NULL); void trace(JSTracer *trc); diff --git a/js/src/jsapi.h b/js/src/jsapi.h index c103ec36787b..5f9fc95c845d 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -2944,7 +2944,7 @@ struct JSClass { * with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was * prevously allowed, but is now an ES5 violation and thus unsupported. */ -#define JSCLASS_GLOBAL_SLOT_COUNT (JSProto_LIMIT * 3 + 25) +#define JSCLASS_GLOBAL_SLOT_COUNT (JSProto_LIMIT * 3 + 26) #define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \ (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n))) #define JSCLASS_GLOBAL_FLAGS \ diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 4b0ff4950952..12c6824f80f1 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -27,6 +27,7 @@ #include "jswrapper.h" #include "builtin/Eval.h" +#include "builtin/Object.h" #include "frontend/BytecodeCompiler.h" #include "frontend/TokenStream.h" #include "gc/Marking.h" @@ -195,40 +196,51 @@ ResolveInterpretedFunctionPrototype(JSContext *cx, HandleObject obj) JS_ASSERT(!fun->isFunctionPrototype()); #endif - /* - * Assert that fun is not a compiler-created function object, which - * must never leak to script or embedding code and then be mutated. - * Also assert that obj is not bound, per the ES5 15.3.4.5 ref above. - */ + // Assert that fun is not a compiler-created function object, which + // must never leak to script or embedding code and then be mutated. + // Also assert that obj is not bound, per the ES5 15.3.4.5 ref above. JS_ASSERT(!IsInternalFunctionObject(obj)); JS_ASSERT(!obj->isBoundFunction()); - /* - * Make the prototype object an instance of Object with the same parent - * as the function object itself. - */ - JSObject *objProto = obj->global().getOrCreateObjectPrototype(cx); + // Make the prototype object an instance of Object with the same parent as + // the function object itself, unless the function is an ES6 generator. In + // that case, per the 15 July 2013 ES6 draft, section 15.19.3, its parent is + // the GeneratorObjectPrototype singleton. + bool isStarGenerator = obj->as().isStarGenerator(); + JSObject *objProto; + if (isStarGenerator) + objProto = obj->global().getOrCreateStarGeneratorObjectPrototype(cx); + else + objProto = obj->global().getOrCreateObjectPrototype(cx); if (!objProto) return NULL; - RootedObject proto(cx, NewObjectWithGivenProto(cx, &JSObject::class_, objProto, NULL, SingletonObject)); + Class *clasp = &JSObject::class_; + + RootedObject proto(cx, NewObjectWithGivenProto(cx, clasp, objProto, NULL, SingletonObject)); if (!proto) return NULL; - /* - * Per ES5 15.3.5.2 a user-defined function's .prototype property is - * initially non-configurable, non-enumerable, and writable. Per ES5 13.2 - * the prototype's .constructor property is configurable, non-enumerable, - * and writable. - */ + // Per ES5 15.3.5.2 a user-defined function's .prototype property is + // initially non-configurable, non-enumerable, and writable. RootedValue protoVal(cx, ObjectValue(*proto)); - RootedValue objVal(cx, ObjectValue(*obj)); if (!JSObject::defineProperty(cx, obj, cx->names().classPrototype, protoVal, JS_PropertyStub, JS_StrictPropertyStub, - JSPROP_PERMANENT) || - !JSObject::defineProperty(cx, proto, cx->names().constructor, - objVal, JS_PropertyStub, JS_StrictPropertyStub, 0)) + JSPROP_PERMANENT)) { - return NULL; + return NULL; + } + + // Per ES5 13.2 the prototype's .constructor property is configurable, + // non-enumerable, and writable. However, per the 15 July 2013 ES6 draft, + // section 15.19.3, the .prototype of a generator function does not link + // back with a .constructor. + if (!isStarGenerator) { + RootedValue objVal(cx, ObjectValue(*obj)); + if (!JSObject::defineProperty(cx, proto, cx->names().constructor, + objVal, JS_PropertyStub, JS_StrictPropertyStub, 0)) + { + return NULL; + } } return proto; @@ -326,12 +338,15 @@ bool js::XDRInterpretedFunction(XDRState *xdr, HandleObject enclosingScope, HandleScript enclosingScript, MutableHandleObject objp) { + enum FirstWordFlag { + HasAtom = 0x1, + IsStarGenerator = 0x2 + }; + /* NB: Keep this in sync with CloneFunctionAndScript. */ RootedAtom atom(xdr->cx()); - uint32_t firstword; /* flag telling whether fun->atom is non-null, - plus for fun->u.i.skipmin, fun->u.i.wrapper, - and 14 bits reserved for future use */ - uint32_t flagsword; /* word for argument count and fun->flags */ + uint32_t firstword = 0; /* bitmask of FirstWordFlag */ + uint32_t flagsword = 0; /* word for argument count and fun->flags */ JSContext *cx = xdr->cx(); RootedFunction fun(cx); @@ -346,24 +361,38 @@ js::XDRInterpretedFunction(XDRState *xdr, HandleObject enclosingScope, Han } return false; } - firstword = !!fun->atom(); + if (fun->atom()) + firstword |= HasAtom; + if (fun->isStarGenerator()) + firstword |= IsStarGenerator; script = fun->getOrCreateScript(cx); if (!script) return false; atom = fun->atom(); flagsword = (fun->nargs << 16) | fun->flags; + + if (!xdr->codeUint32(&firstword)) + return false; } else { - fun = NewFunction(cx, NullPtr(), NULL, 0, JSFunction::INTERPRETED, NullPtr(), NullPtr(), - JSFunction::FinalizeKind, TenuredObject); + if (!xdr->codeUint32(&firstword)) + return false; + + JSObject *proto = NULL; + if (firstword & IsStarGenerator) { + proto = cx->global()->getOrCreateStarGeneratorFunctionPrototype(cx); + if (!proto) + return false; + } + fun = NewFunctionWithProto(cx, NullPtr(), NULL, 0, JSFunction::INTERPRETED, + NullPtr(), NullPtr(), proto, + JSFunction::FinalizeKind, TenuredObject); if (!fun) return false; atom = NULL; script = NULL; } - if (!xdr->codeUint32(&firstword)) - return false; - if ((firstword & 1U) && !XDRAtom(xdr, &atom)) + if ((firstword & HasAtom) && !XDRAtom(xdr, &atom)) return false; if (!xdr->codeUint32(&flagsword)) return false; @@ -398,10 +427,15 @@ JSObject * js::CloneFunctionAndScript(JSContext *cx, HandleObject enclosingScope, HandleFunction srcFun) { /* NB: Keep this in sync with XDRInterpretedFunction. */ - - RootedFunction clone(cx, NewFunction(cx, NullPtr(), NULL, 0, - JSFunction::INTERPRETED, NullPtr(), NullPtr(), - JSFunction::FinalizeKind, TenuredObject)); + JSObject *cloneProto = NULL; + if (srcFun->isStarGenerator()) { + cloneProto = cx->global()->getOrCreateStarGeneratorFunctionPrototype(cx); + if (!cloneProto) + return NULL; + } + RootedFunction clone(cx, NewFunctionWithProto(cx, NullPtr(), NULL, 0, JSFunction::INTERPRETED, + NullPtr(), NullPtr(), cloneProto, + JSFunction::FinalizeKind, TenuredObject)); if (!clone) return NULL; @@ -604,7 +638,7 @@ js::FunctionToString(JSContext *cx, HandleFunction fun, bool bodyOnly, bool lamb return NULL; } if (!fun->isArrow()) { - if (!out.append("function ")) + if (!(fun->isStarGenerator() ? out.append("function* ") : out.append("function "))) return NULL; } if (fun->atom()) { @@ -794,6 +828,9 @@ fun_toSource(JSContext *cx, unsigned argc, Value *vp) if (!obj) return false; + if (!obj->is() && !obj->is()) + return obj_toSource(cx, argc, vp); + RootedString str(cx, fun_toStringHelper(cx, obj, JS_DONT_PRETTY_PRINT)); if (!str) return false; @@ -1326,8 +1363,8 @@ const JSFunctionSpec js::function_methods[] = { JS_FS_END }; -bool -js::Function(JSContext *cx, unsigned argc, Value *vp) +static bool +FunctionConstructor(JSContext *cx, unsigned argc, Value *vp, GeneratorKind generatorKind) { CallArgs args = CallArgsFromVp(argc, vp); RootedString arg(cx); // used multiple times below @@ -1344,6 +1381,9 @@ js::Function(JSContext *cx, unsigned argc, Value *vp) bool hasRest = false; + bool isStarGenerator = generatorKind == StarGenerator; + JS_ASSERT(generatorKind != LegacyGenerator); + const char *filename; unsigned lineno; JSPrincipals *originPrincipals; @@ -1436,6 +1476,7 @@ js::Function(JSContext *cx, unsigned argc, Value *vp) */ TokenStream ts(cx, options, collected_args.get(), args_length, /* strictModeGetter = */ NULL); + bool yieldIsValidName = ts.versionNumber() < JSVERSION_1_7 && !isStarGenerator; /* The argument string may be empty or contain no tokens. */ TokenKind tt = ts.getToken(); @@ -1450,14 +1491,14 @@ js::Function(JSContext *cx, unsigned argc, Value *vp) return false; } - if (tt == TOK_YIELD && ts.versionNumber() < JSVERSION_1_7) + if (tt == TOK_YIELD && yieldIsValidName) tt = TOK_NAME; if (tt != TOK_NAME) { if (tt == TOK_TRIPLEDOT) { hasRest = true; tt = ts.getToken(); - if (tt == TOK_YIELD && ts.versionNumber() < JSVERSION_1_7) + if (tt == TOK_YIELD && yieldIsValidName) tt = TOK_NAME; if (tt != TOK_NAME) { if (tt != TOK_ERROR) @@ -1518,24 +1559,47 @@ js::Function(JSContext *cx, unsigned argc, Value *vp) * and so would a call to f from another top-level's script or function. */ RootedAtom anonymousAtom(cx, cx->names().anonymous); - RootedFunction fun(cx, NewFunction(cx, NullPtr(), NULL, 0, JSFunction::INTERPRETED_LAMBDA, - global, anonymousAtom, JSFunction::FinalizeKind, - TenuredObject)); + JSObject *proto = NULL; + if (isStarGenerator) { + proto = global->getOrCreateStarGeneratorFunctionPrototype(cx); + if (!proto) + return false; + } + RootedFunction fun(cx, NewFunctionWithProto(cx, NullPtr(), NULL, 0, + JSFunction::INTERPRETED_LAMBDA, global, + anonymousAtom, proto, + JSFunction::FinalizeKind, TenuredObject)); if (!fun) return false; if (hasRest) fun->setHasRest(); - bool ok = frontend::CompileFunctionBody(cx, &fun, options, formals, chars, length); + bool ok; + if (isStarGenerator) + ok = frontend::CompileStarGeneratorBody(cx, &fun, options, formals, chars, length); + else + ok = frontend::CompileFunctionBody(cx, &fun, options, formals, chars, length); args.rval().setObject(*fun); return ok; } +bool +js::Function(JSContext *cx, unsigned argc, Value *vp) +{ + return FunctionConstructor(cx, argc, vp, NotGenerator); +} + +bool +js::Generator(JSContext *cx, unsigned argc, Value *vp) +{ + return FunctionConstructor(cx, argc, vp, StarGenerator); +} + bool JSFunction::isBuiltinFunctionConstructor() { - return maybeNative() == Function; + return maybeNative() == Function || maybeNative() == Generator; } JSFunction * @@ -1543,6 +1607,17 @@ js::NewFunction(ExclusiveContext *cx, HandleObject funobjArg, Native native, uns JSFunction::Flags flags, HandleObject parent, HandleAtom atom, gc::AllocKind allocKind /* = JSFunction::FinalizeKind */, NewObjectKind newKind /* = GenericObject */) +{ + return NewFunctionWithProto(cx, funobjArg, native, nargs, flags, parent, atom, NULL, + allocKind, newKind); +} + +JSFunction * +js::NewFunctionWithProto(ExclusiveContext *cx, HandleObject funobjArg, Native native, + unsigned nargs, JSFunction::Flags flags, HandleObject parent, + HandleAtom atom, JSObject *proto, + gc::AllocKind allocKind /* = JSFunction::FinalizeKind */, + NewObjectKind newKind /* = GenericObject */) { JS_ASSERT(allocKind == JSFunction::FinalizeKind || allocKind == JSFunction::ExtendedFinalizeKind); JS_ASSERT(sizeof(JSFunction) <= gc::Arena::thingSize(JSFunction::FinalizeKind)); @@ -1559,7 +1634,7 @@ js::NewFunction(ExclusiveContext *cx, HandleObject funobjArg, Native native, uns // that hasSingletonType implies isInterpreted. if (native && !IsAsmJSModuleNative(native)) newKind = SingletonObject; - funobj = NewObjectWithClassProto(cx, &JSFunction::class_, NULL, + funobj = NewObjectWithClassProto(cx, &JSFunction::class_, proto, SkipScopeParent(parent), allocKind, newKind); if (!funobj) return NULL; @@ -1602,7 +1677,13 @@ js::CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent, return NULL; NewObjectKind newKind = useSameScript ? newKindArg : SingletonObject; - JSObject *cloneobj = NewObjectWithClassProto(cx, &JSFunction::class_, NULL, + JSObject *cloneProto = NULL; + if (fun->isStarGenerator()) { + cloneProto = cx->global()->getOrCreateStarGeneratorFunctionPrototype(cx); + if (!cloneProto) + return NULL; + } + JSObject *cloneobj = NewObjectWithClassProto(cx, &JSFunction::class_, cloneProto, SkipScopeParent(parent), allocKind, newKind); if (!cloneobj) return NULL; diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 1dd31b496e3d..fb37338a9332 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -416,12 +416,22 @@ namespace js { extern bool Function(JSContext *cx, unsigned argc, Value *vp); +extern bool +Generator(JSContext *cx, unsigned argc, Value *vp); + extern JSFunction * NewFunction(ExclusiveContext *cx, HandleObject funobj, JSNative native, unsigned nargs, JSFunction::Flags flags, HandleObject parent, HandleAtom atom, gc::AllocKind allocKind = JSFunction::FinalizeKind, NewObjectKind newKind = GenericObject); +// If proto is NULL, Function.prototype is used instead. +extern JSFunction * +NewFunctionWithProto(ExclusiveContext *cx, HandleObject funobj, JSNative native, unsigned nargs, + JSFunction::Flags flags, HandleObject parent, HandleAtom atom, + JSObject *proto, gc::AllocKind allocKind = JSFunction::FinalizeKind, + NewObjectKind newKind = GenericObject); + extern JSFunction * DefineFunction(JSContext *cx, HandleObject obj, HandleId id, JSNative native, unsigned nargs, unsigned flags, diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index f4f8ec349abc..f9712af0e9f5 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -958,7 +958,7 @@ const JSFunctionSpec ElementIteratorObject::methods[] = { }; static bool -CloseGenerator(JSContext *cx, HandleObject genobj); +CloseLegacyGenerator(JSContext *cx, HandleObject genobj); bool js::ValueToIterator(JSContext *cx, unsigned flags, MutableHandleValue vp) @@ -999,6 +999,30 @@ js::ValueToIterator(JSContext *cx, unsigned flags, MutableHandleValue vp) return GetIterator(cx, obj, flags, vp); } +static bool +IsGenerator(HandleValue v) +{ + return v.isObject() && v.toObject().is(); +} + +static bool +IsLegacyGenerator(HandleObject obj) +{ + if (!obj->is()) + return false; + JSGenerator *gen = obj->as().getGenerator(); + return gen->regs.fp()->script()->isLegacyGenerator(); +} + +static bool +IsLegacyGenerator(HandleValue v) +{ + if (!IsGenerator(v)) + return false; + JSGenerator *gen = v.toObject().as().getGenerator(); + return gen->regs.fp()->script()->isLegacyGenerator(); +} + bool js::CloseIterator(JSContext *cx, HandleObject obj) { @@ -1020,9 +1044,8 @@ js::CloseIterator(JSContext *cx, HandleObject obj) */ ni->props_cursor = ni->props_array; } - } else if (obj->is()) { - // FIXME: Only close legacy generators. - return CloseGenerator(cx, obj); + } else if (IsLegacyGenerator(obj)) { + return CloseLegacyGenerator(cx, obj); } return true; } @@ -1477,15 +1500,25 @@ js_NewGenerator(JSContext *cx, const FrameRegs &stackRegs) JS_ASSERT(stackfp->script()->isGenerator()); - if (stackfp->script()->isStarGenerator()) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_ES6_UNIMPLEMENTED); - return NULL; - } - Rooted global(cx, &stackfp->global()); RootedObject obj(cx); - { - JSObject *proto = global->getOrCreateGeneratorPrototype(cx); + if (stackfp->script()->isStarGenerator()) { + RootedValue pval(cx); + RootedObject fun(cx, stackfp->fun()); + // FIXME: This would be faster if we could avoid doing a lookup to get + // the prototype for the instance. Bug 906600. + if (!JSObject::getProperty(cx, fun, fun, cx->names().classPrototype, &pval)) + return NULL; + JSObject *proto = pval.isObject() ? &pval.toObject() : NULL; + if (!proto) { + proto = global->getOrCreateStarGeneratorObjectPrototype(cx); + if (!proto) + return NULL; + } + obj = NewObjectWithGivenProto(cx, &GeneratorObject::class_, proto, global); + } else { + JS_ASSERT(stackfp->script()->isLegacyGenerator()); + JSObject *proto = global->getOrCreateLegacyGeneratorObjectPrototype(cx); if (!proto) return NULL; obj = NewObjectWithGivenProto(cx, &GeneratorObject::class_, proto, global); @@ -1623,66 +1656,17 @@ SendToGenerator(JSContext *cx, JSGeneratorOp op, HandleObject obj, } static bool -CloseGenerator(JSContext *cx, HandleObject obj) +CloseLegacyGenerator(JSContext *cx, HandleObject obj) { + JS_ASSERT(IsLegacyGenerator(obj)); + JSGenerator *gen = obj->as().getGenerator(); - if (!gen) { - /* Generator prototype object. */ - return true; - } - - // FIXME: Assert that gen is a legacy generator. - if (gen->state == JSGEN_CLOSED) return true; return SendToGenerator(cx, JSGENOP_CLOSE, obj, gen, JS::UndefinedHandleValue); } -JS_ALWAYS_INLINE bool -IsGenerator(HandleValue v) -{ - return v.isObject() && v.toObject().is(); -} - -JS_ALWAYS_INLINE bool -generator_send_impl(JSContext *cx, CallArgs args) -{ - // FIXME: Change assertion to IsLegacyGenerator(). - JS_ASSERT(IsGenerator(args.thisv())); - - RootedObject thisObj(cx, &args.thisv().toObject()); - - JSGenerator *gen = thisObj->as().getGenerator(); - if (!gen || gen->state == JSGEN_CLOSED) { - /* This happens when obj is the generator prototype. See bug 352885. */ - return js_ThrowStopIteration(cx); - } - - if (gen->state == JSGEN_NEWBORN && args.hasDefined(0)) { - RootedValue val(cx, args[0]); - js_ReportValueError(cx, JSMSG_BAD_GENERATOR_SEND, - JSDVG_SEARCH_STACK, val, NullPtr()); - return false; - } - - // FIXME: next() takes the send value as an optional argument in ES6 - // generator objects. - if (!SendToGenerator(cx, JSGENOP_SEND, thisObj, gen, args.get(0))) - return false; - - args.rval().set(gen->fp->returnValue()); - return true; -} - -bool -generator_send(JSContext *cx, unsigned argc, Value *vp) -{ - // FIXME: send() is only a method on legacy generator objects. - CallArgs args = CallArgsFromVp(argc, vp); - return CallNonGenericMethod(cx, args); -} - JS_ALWAYS_INLINE bool generator_next_impl(JSContext *cx, CallArgs args) { @@ -1691,12 +1675,17 @@ generator_next_impl(JSContext *cx, CallArgs args) RootedObject thisObj(cx, &args.thisv().toObject()); JSGenerator *gen = thisObj->as().getGenerator(); - if (!gen || gen->state == JSGEN_CLOSED) { - /* This happens when obj is the generator prototype. See bug 352885. */ + if (gen->state == JSGEN_CLOSED) return js_ThrowStopIteration(cx); + + if (gen->state == JSGEN_NEWBORN && args.hasDefined(0)) { + RootedValue val(cx, args[0]); + js_ReportValueError(cx, JSMSG_BAD_GENERATOR_SEND, + JSDVG_SEARCH_STACK, val, NullPtr()); + return false; } - if (!SendToGenerator(cx, JSGENOP_NEXT, thisObj, gen, JS::UndefinedHandleValue)) + if (!SendToGenerator(cx, JSGENOP_SEND, thisObj, gen, args.get(0))) return false; args.rval().set(gen->fp->returnValue()); @@ -1718,8 +1707,7 @@ generator_throw_impl(JSContext *cx, CallArgs args) RootedObject thisObj(cx, &args.thisv().toObject()); JSGenerator *gen = thisObj->as().getGenerator(); - if (!gen || gen->state == JSGEN_CLOSED) { - /* This happens when obj is the generator prototype. See bug 352885. */ + if (gen->state == JSGEN_CLOSED) { cx->setPendingException(args.length() >= 1 ? args[0] : UndefinedValue()); return false; } @@ -1741,14 +1729,12 @@ generator_throw(JSContext *cx, unsigned argc, Value *vp) JS_ALWAYS_INLINE bool generator_close_impl(JSContext *cx, CallArgs args) { - // FIXME: Change assertion to IsLegacyGenerator(). - JS_ASSERT(IsGenerator(args.thisv())); + JS_ASSERT(IsLegacyGenerator(args.thisv())); RootedObject thisObj(cx, &args.thisv().toObject()); JSGenerator *gen = thisObj->as().getGenerator(); - if (!gen || gen->state == JSGEN_CLOSED) { - /* This happens when obj is the generator prototype. See bug 352885. */ + if (gen->state == JSGEN_CLOSED) { args.rval().setUndefined(); return true; } @@ -1769,22 +1755,47 @@ generator_close_impl(JSContext *cx, CallArgs args) bool generator_close(JSContext *cx, unsigned argc, Value *vp) { - // FIXME: close() is only a method on legacy generator objects. CallArgs args = CallArgsFromVp(argc, vp); - return CallNonGenericMethod(cx, args); + return CallNonGenericMethod(cx, args); } #define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT) -static const JSFunctionSpec generator_methods[] = { +static const JSFunctionSpec legacy_generator_methods[] = { JS_FN("iterator", iterator_iterator, 0, 0), - JS_FN("next", generator_next, 0,JSPROP_ROPERM), - JS_FN("send", generator_send, 1,JSPROP_ROPERM), + JS_FN("next", generator_next, 1,JSPROP_ROPERM), + // Send is exactly the same as next. + JS_FN("send", generator_next, 1,JSPROP_ROPERM), JS_FN("throw", generator_throw, 1,JSPROP_ROPERM), JS_FN("close", generator_close, 0,JSPROP_ROPERM), JS_FS_END }; +static const JSFunctionSpec star_generator_methods[] = { + JS_FN("iterator", iterator_iterator, 0, 0), + JS_FN("next", generator_next, 1,JSPROP_ROPERM), + JS_FN("throw", generator_throw, 1,JSPROP_ROPERM), + JS_FS_END +}; + +static JSObject* +NewObjectWithObjectPrototype(JSContext *cx, Handle global) +{ + JSObject *proto = global->getOrCreateObjectPrototype(cx); + if (!proto) + return NULL; + return NewObjectWithGivenProto(cx, &JSObject::class_, proto, global); +} + +static JSObject* +NewObjectWithFunctionPrototype(JSContext *cx, Handle global) +{ + JSObject *proto = global->getOrCreateFunctionPrototype(cx); + if (!proto) + return NULL; + return NewObjectWithGivenProto(cx, &JSObject::class_, proto, global); +} + /* static */ bool GlobalObject::initIteratorClasses(JSContext *cx, Handle global) { @@ -1826,11 +1837,41 @@ GlobalObject::initIteratorClasses(JSContext *cx, Handle global) global->setReservedSlot(ELEMENT_ITERATOR_PROTO, ObjectValue(*proto)); } - if (global->getSlot(GENERATOR_PROTO).isUndefined()) { - proto = global->createBlankPrototype(cx, &GeneratorObject::class_); - if (!proto || !DefinePropertiesAndBrand(cx, proto, NULL, generator_methods)) + if (global->getSlot(LEGACY_GENERATOR_OBJECT_PROTO).isUndefined()) { + proto = NewObjectWithObjectPrototype(cx, global); + if (!proto || !DefinePropertiesAndBrand(cx, proto, NULL, legacy_generator_methods)) return false; - global->setReservedSlot(GENERATOR_PROTO, ObjectValue(*proto)); + global->setReservedSlot(LEGACY_GENERATOR_OBJECT_PROTO, ObjectValue(*proto)); + } + + if (global->getSlot(STAR_GENERATOR_OBJECT_PROTO).isUndefined()) { + RootedObject genObjectProto(cx, NewObjectWithObjectPrototype(cx, global)); + if (!genObjectProto) + return false; + if (!DefinePropertiesAndBrand(cx, genObjectProto, NULL, star_generator_methods)) + return false; + + RootedObject genFunctionProto(cx, NewObjectWithFunctionPrototype(cx, global)); + if (!genFunctionProto) + return false; + if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto)) + return false; + + RootedValue function(cx, global->getConstructor(JSProto_Function)); + if (!function.toObjectOrNull()) + return false; + RootedAtom name(cx, cx->names().GeneratorFunction); + RootedObject genFunction(cx, NewFunctionWithProto(cx, NullPtr(), Generator, 1, + JSFunction::NATIVE_CTOR, global, name, + &function.toObject())); + if (!genFunction) + return false; + if (!LinkConstructorAndPrototype(cx, genFunction, genFunctionProto)) + return false; + + global->setSlot(STAR_GENERATOR_OBJECT_PROTO, ObjectValue(*genObjectProto)); + global->setSlot(JSProto_GeneratorFunction, ObjectValue(*genFunction)); + global->setSlot(JSProto_GeneratorFunction + JSProto_LIMIT, ObjectValue(*genFunctionProto)); } if (global->getPrototype(JSProto_StopIteration).isUndefined()) { diff --git a/js/src/jsprototypes.h b/js/src/jsprototypes.h index 034ff6dd469c..6f9bbbb3e085 100644 --- a/js/src/jsprototypes.h +++ b/js/src/jsprototypes.h @@ -69,5 +69,6 @@ macro(ArrayType, 50, js_InitBinaryDataClasses) \ macro(StructType, 51, js_InitBinaryDataClasses) \ macro(ArrayTypeObject, 52, js_InitBinaryDataClasses) \ + macro(GeneratorFunction, 53, js_InitIteratorClasses) \ #endif /* jsprototypes_h */ diff --git a/js/src/jsworkers.cpp b/js/src/jsworkers.cpp index 8179f3a2f353..5cdbe1c890e0 100644 --- a/js/src/jsworkers.cpp +++ b/js/src/jsworkers.cpp @@ -236,7 +236,8 @@ js::StartOffThreadParseScript(JSContext *cx, const CompileOptions &options, // pointers can be changed infallibly after parsing finishes. if (!js_GetClassObject(cx, cx->global(), JSProto_Function, &obj) || !js_GetClassObject(cx, cx->global(), JSProto_Array, &obj) || - !js_GetClassObject(cx, cx->global(), JSProto_RegExp, &obj)) + !js_GetClassObject(cx, cx->global(), JSProto_RegExp, &obj) || + !js_GetClassObject(cx, cx->global(), JSProto_GeneratorFunction, &obj)) { return false; } @@ -244,7 +245,8 @@ js::StartOffThreadParseScript(JSContext *cx, const CompileOptions &options, AutoCompartment ac(cx, global); if (!js_GetClassObject(cx, global, JSProto_Function, &obj) || !js_GetClassObject(cx, global, JSProto_Array, &obj) || - !js_GetClassObject(cx, global, JSProto_RegExp, &obj)) + !js_GetClassObject(cx, global, JSProto_RegExp, &obj) || + !js_GetClassObject(cx, global, JSProto_GeneratorFunction, &obj)) { return false; } diff --git a/js/src/tests/ecma_6/Generators/runtime.js b/js/src/tests/ecma_6/Generators/runtime.js new file mode 100644 index 000000000000..a55c0d1562c1 --- /dev/null +++ b/js/src/tests/ecma_6/Generators/runtime.js @@ -0,0 +1,150 @@ +// This file was written by Andy Wingo and originally +// contributed to V8 as generators-runtime.js, available here: +// +// http://code.google.com/p/v8/source/browse/branches/bleeding_edge/test/mjsunit/harmony/generators-runtime.js + +// Test aspects of the generator runtime. + +// See http://people.mozilla.org/~jorendorff/es6-draft.html#sec-15.19.3. + +function assertSyntaxError(str) { + var msg; + var evil = eval; + try { + // Non-direct eval. + evil(str); + } catch (exc) { + if (exc instanceof SyntaxError) + return; + msg = "Assertion failed: expected SyntaxError, got " + exc; + } + if (msg === undefined) + msg = "Assertion failed: expected SyntaxError, but no exception thrown"; + throw new Error(msg + " - " + str); +} + +function assertFalse(a) { assertEq(a, false) } +function assertTrue(a) { assertEq(a, true) } +function assertNotEq(found, not_expected) { assertFalse(found === expected) } +function assertArrayEq(found, expected) { + assertEq(found.length, expected.length); + for (var i = 0; i < expected.length; i++) + assertEq(found[i], expected[i]); +} + + +function f() { } +function* g() { yield 1; } +var GeneratorFunctionPrototype = Object.getPrototypeOf(g); +var GeneratorFunction = GeneratorFunctionPrototype.constructor; +var GeneratorObjectPrototype = GeneratorFunctionPrototype.prototype; + + +// A generator function should have the same set of properties as any +// other function. +function TestGeneratorFunctionInstance() { + var f_own_property_names = Object.getOwnPropertyNames(f); + var g_own_property_names = Object.getOwnPropertyNames(g); + + f_own_property_names.sort(); + g_own_property_names.sort(); + + assertArrayEq(f_own_property_names, g_own_property_names); + var i; + for (i = 0; i < f_own_property_names.length; i++) { + var prop = f_own_property_names[i]; + var f_desc = Object.getOwnPropertyDescriptor(f, prop); + var g_desc = Object.getOwnPropertyDescriptor(g, prop); + assertEq(f_desc.configurable, g_desc.configurable, prop); + assertEq(f_desc.writable, g_desc.writable, prop); + assertEq(f_desc.enumerable, g_desc.enumerable, prop); + } +} +TestGeneratorFunctionInstance(); + + +// Generators have an additional object interposed in the chain between +// themselves and Function.prototype. +function TestGeneratorFunctionPrototype() { + // Sanity check. + assertEq(Object.getPrototypeOf(f), Function.prototype); + assertNotEq(GeneratorFunctionPrototype, Function.prototype); + assertEq(Object.getPrototypeOf(GeneratorFunctionPrototype), + Function.prototype); + assertEq(Object.getPrototypeOf(function* () {}), + GeneratorFunctionPrototype); +} +TestGeneratorFunctionPrototype(); + + +// Functions that we associate with generator objects are actually defined by +// a common prototype. +function TestGeneratorObjectPrototype() { + assertEq(Object.getPrototypeOf(GeneratorObjectPrototype), + Object.prototype); + assertEq(Object.getPrototypeOf((function*(){yield 1}).prototype), + GeneratorObjectPrototype); + + var expected_property_names = ["iterator", "next", "throw", "constructor"]; + var found_property_names = + Object.getOwnPropertyNames(GeneratorObjectPrototype); + + expected_property_names.sort(); + found_property_names.sort(); + + assertArrayEq(found_property_names, expected_property_names); +} +TestGeneratorObjectPrototype(); + + +// This tests the object that would be called "GeneratorFunction", if it were +// like "Function". +function TestGeneratorFunction() { + assertEq(GeneratorFunctionPrototype, GeneratorFunction.prototype); + assertTrue(g instanceof GeneratorFunction); + + assertEq(Function, Object.getPrototypeOf(GeneratorFunction)); + assertTrue(g instanceof Function); + + assertEq("function* g() { yield 1; }", g.toString()); + + // Not all functions are generators. + assertTrue(f instanceof Function); // Sanity check. + assertFalse(f instanceof GeneratorFunction); + + assertTrue((new GeneratorFunction()) instanceof GeneratorFunction); + assertTrue(GeneratorFunction() instanceof GeneratorFunction); + + assertTrue(GeneratorFunction('yield 1') instanceof GeneratorFunction); + assertTrue(GeneratorFunction('return 1') instanceof GeneratorFunction); + assertTrue(GeneratorFunction('a', 'yield a') instanceof GeneratorFunction); + assertTrue(GeneratorFunction('a', 'return a') instanceof GeneratorFunction); + assertTrue(GeneratorFunction('a', 'return a') instanceof GeneratorFunction); + assertSyntaxError("GeneratorFunction('yield', 'return yield')"); + assertTrue(GeneratorFunction('with (x) return foo;') instanceof GeneratorFunction); + assertSyntaxError("GeneratorFunction('\"use strict\"; with (x) return foo;')"); + + // Doesn't matter particularly what string gets serialized, as long + // as it contains "function*" and "yield 10". + assertEq(GeneratorFunction('yield 10').toString(), + "function* anonymous() {\nyield 10\n}"); +} +TestGeneratorFunction(); + + +function TestPerGeneratorPrototype() { + assertNotEq((function*(){}).prototype, (function*(){}).prototype); + assertNotEq((function*(){}).prototype, g.prototype); + assertEq(typeof GeneratorFunctionPrototype, "object"); + assertEq(g.prototype.__proto__.constructor, GeneratorFunctionPrototype, "object"); + assertEq(Object.getPrototypeOf(g.prototype), GeneratorObjectPrototype); + assertFalse(g.prototype instanceof Function); + assertEq(typeof (g.prototype), "object"); + + assertArrayEq(Object.getOwnPropertyNames(g.prototype), []); +} +TestPerGeneratorPrototype(); + + +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/tests/js1_7/extensions/regress-352885-02.js b/js/src/tests/js1_7/extensions/regress-352885-02.js index c6d8bd7805d8..613269bb685f 100644 --- a/js/src/tests/js1_7/extensions/regress-352885-02.js +++ b/js/src/tests/js1_7/extensions/regress-352885-02.js @@ -26,34 +26,35 @@ function test() try { proto.next(); - throw "generatorProto.next() does not throw StopIteration"; + throw "generatorProto.next() does not throw TypeError"; } catch (e) { - if (!(e instanceof StopIteration)) + if (!(e instanceof TypeError)) throw "generatorProto.next() throws unexpected exception: "+uneval(e); } try { proto.send(); - throw "generatorProto.send() does not throw StopIteration"; + throw "generatorProto.send() does not throw TypeError"; } catch (e) { - if (!(e instanceof StopIteration)) + if (!(e instanceof TypeError)) throw "generatorProto.send() throws unexpected exception: "+uneval(e); } var obj = {}; try { proto.throw(obj); - throw "generatorProto.throw(obj) does not throw obj"; + throw "generatorProto.throw(obj) does not throw TypeError"; } catch (e) { - if (e !== obj) + if (!(e instanceof TypeError)) throw "generatorProto.throw() throws unexpected exception: "+uneval(e); } - var obj = {}; try { proto.close(); + throw "generatorProto.close() does not throw TypeError"; } catch (e) { - throw "generatorProto.throw() throws exception: "+uneval(e); + if (!(e instanceof TypeError)) + throw "generatorProto.close() throws unexpected exception: "+uneval(e); } } diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h index a1e948a71efb..ead6e4a90a95 100644 --- a/js/src/vm/GlobalObject.h +++ b/js/src/vm/GlobalObject.h @@ -90,8 +90,9 @@ class GlobalObject : public JSObject /* One-off properties stored after slots for built-ins. */ static const unsigned ELEMENT_ITERATOR_PROTO = FROM_BUFFER_UINT8CLAMPED + 1; - static const unsigned GENERATOR_PROTO = ELEMENT_ITERATOR_PROTO + 1; - static const unsigned MAP_ITERATOR_PROTO = GENERATOR_PROTO + 1; + static const unsigned LEGACY_GENERATOR_OBJECT_PROTO = ELEMENT_ITERATOR_PROTO + 1; + static const unsigned STAR_GENERATOR_OBJECT_PROTO = LEGACY_GENERATOR_OBJECT_PROTO + 1; + static const unsigned MAP_ITERATOR_PROTO = STAR_GENERATOR_OBJECT_PROTO + 1; static const unsigned SET_ITERATOR_PROTO = MAP_ITERATOR_PROTO + 1; static const unsigned COLLATOR_PROTO = SET_ITERATOR_PROTO + 1; static const unsigned NUMBER_FORMAT_PROTO = COLLATOR_PROTO + 1; @@ -364,8 +365,21 @@ class GlobalObject : public JSObject return getOrCreateObject(cx, ELEMENT_ITERATOR_PROTO, initIteratorClasses); } - JSObject *getOrCreateGeneratorPrototype(JSContext *cx) { - return getOrCreateObject(cx, GENERATOR_PROTO, initIteratorClasses); + JSObject *getOrCreateLegacyGeneratorObjectPrototype(JSContext *cx) { + return getOrCreateObject(cx, LEGACY_GENERATOR_OBJECT_PROTO, initIteratorClasses); + } + + JSObject *getOrCreateStarGeneratorObjectPrototype(JSContext *cx) { + return getOrCreateObject(cx, STAR_GENERATOR_OBJECT_PROTO, initIteratorClasses); + } + + JSObject *getOrCreateStarGeneratorFunctionPrototype(JSContext *cx) { + return getOrCreateObject(cx, JSProto_LIMIT + JSProto_GeneratorFunction, + initIteratorClasses); + } + + JSObject *getOrCreateStarGeneratorFunction(JSContext *cx) { + return getOrCreateObject(cx, JSProto_GeneratorFunction, initIteratorClasses); } JSObject *getOrCreateMapIteratorPrototype(JSContext *cx) { From 0e0071eff3c40dc11564e3659a741fb470a58882 Mon Sep 17 00:00:00 2001 From: Daniele Bonetta Date: Mon, 19 Aug 2013 14:51:34 -0700 Subject: [PATCH 50/54] Bug 906773 - More efficient ComputeSliceBounds function. r=nmatsakis --- js/src/builtin/ParallelArray.js | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/js/src/builtin/ParallelArray.js b/js/src/builtin/ParallelArray.js index 5876de0a1bdb..9776d849d546 100644 --- a/js/src/builtin/ParallelArray.js +++ b/js/src/builtin/ParallelArray.js @@ -55,8 +55,12 @@ function ComputeNumChunks(length) { */ function ComputeSliceBounds(numItems, sliceIndex, numSlices) { var sliceWidth = (numItems / numSlices) | 0; - var startIndex = sliceWidth * sliceIndex; - var endIndex = sliceIndex === numSlices - 1 ? numItems : sliceWidth * (sliceIndex + 1); + var extraChunks = (numItems % numSlices) | 0; + + var startIndex = sliceWidth * sliceIndex + std_Math_min(extraChunks, sliceIndex); + var endIndex = startIndex + sliceWidth; + if (sliceIndex < extraChunks) + endIndex += 1; return [startIndex, endIndex]; } @@ -70,10 +74,18 @@ function ComputeSliceBounds(numItems, sliceIndex, numSlices) { */ function ComputeAllSliceBounds(numItems, numSlices) { // FIXME(bug 844890): Use typed arrays here. + var sliceWidth = (numItems / numSlices) | 0; + var extraChunks = (numItems % numSlices) | 0; + var counter = 0; var info = []; - for (var i = 0; i < numSlices; i++) { - var [start, end] = ComputeSliceBounds(numItems, i, numSlices); - ARRAY_PUSH(info, SLICE_INFO(start, end)); + var i = 0; + for (; i < extraChunks; i++) { + ARRAY_PUSH(info, SLICE_INFO(counter, counter + sliceWidth + 1)); + counter += sliceWidth + 1; + } + for (; i < numSlices; i++) { + ARRAY_PUSH(info, SLICE_INFO(counter, counter + sliceWidth)); + counter += sliceWidth; } return info; } From 66e6988bb8ccc12a078d44da771ea6eda74ceb48 Mon Sep 17 00:00:00 2001 From: Mina Almasry Date: Thu, 22 Aug 2013 10:16:07 -0400 Subject: [PATCH 51/54] Bug 907816 - GetCSSValuesForProperty does not use the display keyword table. r=bz --- layout/inspector/src/inDOMUtils.cpp | 8 ++++++-- layout/style/nsCSSPropList.h | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/layout/inspector/src/inDOMUtils.cpp b/layout/inspector/src/inDOMUtils.cpp index 683f1ece92df..76e2a0d323be 100644 --- a/layout/inspector/src/inDOMUtils.cpp +++ b/layout/inspector/src/inDOMUtils.cpp @@ -571,7 +571,9 @@ inDOMUtils::GetCSSValuesForProperty(const nsAString& aProperty, uint32_t propertyParserVariant = nsCSSProps::ParserVariant(propertyID); // Get colors first. GetColorsForProperty(propertyParserVariant, array); - GetKeywordsForProperty(propertyID, array); + if (propertyParserVariant & VARIANT_KEYWORD) { + GetKeywordsForProperty(propertyID, array); + } GetOtherValuesForProperty(propertyParserVariant, array); } else { // Property is shorthand. @@ -585,7 +587,9 @@ inDOMUtils::GetCSSValuesForProperty(const nsAString& aProperty, } CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(subproperty, propertyID) { uint32_t propertyParserVariant = nsCSSProps::ParserVariant(*subproperty); - GetKeywordsForProperty(*subproperty, array); + if (propertyParserVariant & VARIANT_KEYWORD) { + GetKeywordsForProperty(*subproperty, array); + } GetOtherValuesForProperty(propertyParserVariant, array); } } diff --git a/layout/style/nsCSSPropList.h b/layout/style/nsCSSPropList.h index feb03a594b9b..a8d48a23e839 100644 --- a/layout/style/nsCSSPropList.h +++ b/layout/style/nsCSSPropList.h @@ -2861,7 +2861,7 @@ CSS_PROP_DISPLAY( CSS_PROPERTY_PARSE_FUNCTION, "", 0, - kDisplayKTable, + nullptr, offsetof(nsStyleDisplay, mSpecifiedTransform), eStyleAnimType_Custom) CSS_PROP_DISPLAY( From e4c10c125fc62ffabe5e1dc90830fb52adce7e94 Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Thu, 22 Aug 2013 16:14:40 +0100 Subject: [PATCH 52/54] Backed out changeset 6a182052fafe (bug 904701) for warnings as errors failures on a CLOSED TREE --- js/src/builtin/Object.cpp | 4 +- js/src/builtin/Object.h | 6 +- js/src/frontend/BytecodeCompiler.cpp | 24 +-- js/src/frontend/BytecodeCompiler.h | 3 - js/src/frontend/Parser.cpp | 18 +- js/src/frontend/Parser.h | 3 +- js/src/jsapi.h | 2 +- js/src/jsfun.cpp | 177 +++++---------- js/src/jsfun.h | 10 - js/src/jsiter.cpp | 203 +++++++----------- js/src/jsprototypes.h | 1 - js/src/jsworkers.cpp | 6 +- js/src/tests/ecma_6/Generators/runtime.js | 150 ------------- .../js1_7/extensions/regress-352885-02.js | 17 +- js/src/vm/GlobalObject.h | 22 +- 15 files changed, 156 insertions(+), 490 deletions(-) delete mode 100644 js/src/tests/ecma_6/Generators/runtime.js diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index 970f40eac885..51fc64aca663 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -87,8 +87,8 @@ obj_propertyIsEnumerable(JSContext *cx, unsigned argc, Value *vp) } #if JS_HAS_TOSOURCE -bool -js::obj_toSource(JSContext *cx, unsigned argc, Value *vp) +static bool +obj_toSource(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); JS_CHECK_RECURSION(cx, return false); diff --git a/js/src/builtin/Object.h b/js/src/builtin/Object.h index 892dce5c2199..79e784f78ced 100644 --- a/js/src/builtin/Object.h +++ b/js/src/builtin/Object.h @@ -14,14 +14,10 @@ namespace js { extern const JSFunctionSpec object_methods[]; extern const JSFunctionSpec object_static_methods[]; -// Object constructor native. Exposed only so the JIT can know its address. +/* Object constructor native. Exposed only so the JIT can know its address. */ extern bool obj_construct(JSContext *cx, unsigned argc, js::Value *vp); -// Object.prototype.toSource. Exposed so that Function.prototype.toSource can chain up. -extern bool -obj_toSource(JSContext *cx, unsigned argc, js::Value *vp); - } /* namespace js */ #endif /* builtin_Object_h */ diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp index 3a199d261f6c..27ffbb044061 100644 --- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -452,10 +452,9 @@ frontend::CompileLazyFunction(JSContext *cx, LazyScript *lazy, const jschar *cha // Compile a JS function body, which might appear as the value of an event // handler attribute in an HTML tag, or in a Function() constructor. -static bool -CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileOptions options, - const AutoNameVector &formals, const jschar *chars, size_t length, - GeneratorKind generatorKind) +bool +frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileOptions options, + const AutoNameVector &formals, const jschar *chars, size_t length) { #if JS_TRACE_LOGGING js::AutoTraceLog logger(js::TraceLogging::defaultLogger(), @@ -522,7 +521,7 @@ CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileOptions opt ParseNode *fn; while (true) { Directives newDirectives = directives; - fn = parser.standaloneFunctionBody(fun, formals, generatorKind, directives, &newDirectives); + fn = parser.standaloneFunctionBody(fun, formals, NotGenerator, directives, &newDirectives); if (fn) break; @@ -587,18 +586,3 @@ CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileOptions opt return true; } - -bool -frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileOptions options, - const AutoNameVector &formals, const jschar *chars, size_t length) -{ - return CompileFunctionBody(cx, fun, options, formals, chars, length, NotGenerator); -} - -bool -frontend::CompileStarGeneratorBody(JSContext *cx, MutableHandleFunction fun, - CompileOptions options, const AutoNameVector &formals, - const jschar *chars, size_t length) -{ - return CompileFunctionBody(cx, fun, options, formals, chars, length, StarGenerator); -} diff --git a/js/src/frontend/BytecodeCompiler.h b/js/src/frontend/BytecodeCompiler.h index 109e8031cecc..12721bcfec09 100644 --- a/js/src/frontend/BytecodeCompiler.h +++ b/js/src/frontend/BytecodeCompiler.h @@ -33,9 +33,6 @@ CompileLazyFunction(JSContext *cx, LazyScript *lazy, const jschar *chars, size_t bool CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileOptions options, const AutoNameVector &formals, const jschar *chars, size_t length); -bool -CompileStarGeneratorBody(JSContext *cx, MutableHandleFunction fun, CompileOptions options, - const AutoNameVector &formals, const jschar *chars, size_t length); /* * This should be called while still on the main thread if compilation will diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index a2fa03b8fc95..856249dbcffd 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -1243,7 +1243,7 @@ struct BindData template JSFunction * Parser::newFunction(GenericParseContext *pc, HandleAtom atom, - FunctionSyntaxKind kind, JSObject *proto) + FunctionSyntaxKind kind) { JS_ASSERT_IF(kind == Statement, atom != NULL); @@ -1266,8 +1266,8 @@ Parser::newFunction(GenericParseContext *pc, HandleAtom atom, : (kind == Arrow) ? JSFunction::INTERPRETED_LAMBDA_ARROW : JSFunction::INTERPRETED; - fun = NewFunctionWithProto(context, NullPtr(), NULL, 0, flags, parent, atom, proto, - JSFunction::FinalizeKind, MaybeSingletonObject); + fun = NewFunction(context, NullPtr(), NULL, 0, flags, parent, atom, + JSFunction::FinalizeKind, MaybeSingletonObject); if (!fun) return NULL; if (options().selfHostingMode) @@ -1993,17 +1993,7 @@ Parser::functionDef(HandlePropertyName funName, const TokenStream: if (bodyProcessed) return pn; - RootedObject proto(context); - if (generatorKind == StarGenerator) { - // If we are off the main thread, the generator meta-objects have - // already been created by js::StartOffThreadParseScript, so cx will not - // be necessary. - JSContext *cx = context->maybeJSContext(); - proto = context->global()->getOrCreateStarGeneratorFunctionPrototype(cx); - if (!proto) - return null(); - } - RootedFunction fun(context, newFunction(pc, funName, kind, proto)); + RootedFunction fun(context, newFunction(pc, funName, kind)); if (!fun) return null(); diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 3421ec5a2153..9b933f7377b5 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -407,8 +407,7 @@ class Parser : private AutoGCRooter, public StrictModeGetter * Create a new function object given parse context (pc) and a name (which * is optional if this is a function expression). */ - JSFunction *newFunction(GenericParseContext *pc, HandleAtom atom, FunctionSyntaxKind kind, - JSObject *proto = NULL); + JSFunction *newFunction(GenericParseContext *pc, HandleAtom atom, FunctionSyntaxKind kind); void trace(JSTracer *trc); diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 5f9fc95c845d..c103ec36787b 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -2944,7 +2944,7 @@ struct JSClass { * with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was * prevously allowed, but is now an ES5 violation and thus unsupported. */ -#define JSCLASS_GLOBAL_SLOT_COUNT (JSProto_LIMIT * 3 + 26) +#define JSCLASS_GLOBAL_SLOT_COUNT (JSProto_LIMIT * 3 + 25) #define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \ (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n))) #define JSCLASS_GLOBAL_FLAGS \ diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 12c6824f80f1..4b0ff4950952 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -27,7 +27,6 @@ #include "jswrapper.h" #include "builtin/Eval.h" -#include "builtin/Object.h" #include "frontend/BytecodeCompiler.h" #include "frontend/TokenStream.h" #include "gc/Marking.h" @@ -196,51 +195,40 @@ ResolveInterpretedFunctionPrototype(JSContext *cx, HandleObject obj) JS_ASSERT(!fun->isFunctionPrototype()); #endif - // Assert that fun is not a compiler-created function object, which - // must never leak to script or embedding code and then be mutated. - // Also assert that obj is not bound, per the ES5 15.3.4.5 ref above. + /* + * Assert that fun is not a compiler-created function object, which + * must never leak to script or embedding code and then be mutated. + * Also assert that obj is not bound, per the ES5 15.3.4.5 ref above. + */ JS_ASSERT(!IsInternalFunctionObject(obj)); JS_ASSERT(!obj->isBoundFunction()); - // Make the prototype object an instance of Object with the same parent as - // the function object itself, unless the function is an ES6 generator. In - // that case, per the 15 July 2013 ES6 draft, section 15.19.3, its parent is - // the GeneratorObjectPrototype singleton. - bool isStarGenerator = obj->as().isStarGenerator(); - JSObject *objProto; - if (isStarGenerator) - objProto = obj->global().getOrCreateStarGeneratorObjectPrototype(cx); - else - objProto = obj->global().getOrCreateObjectPrototype(cx); + /* + * Make the prototype object an instance of Object with the same parent + * as the function object itself. + */ + JSObject *objProto = obj->global().getOrCreateObjectPrototype(cx); if (!objProto) return NULL; - Class *clasp = &JSObject::class_; - - RootedObject proto(cx, NewObjectWithGivenProto(cx, clasp, objProto, NULL, SingletonObject)); + RootedObject proto(cx, NewObjectWithGivenProto(cx, &JSObject::class_, objProto, NULL, SingletonObject)); if (!proto) return NULL; - // Per ES5 15.3.5.2 a user-defined function's .prototype property is - // initially non-configurable, non-enumerable, and writable. + /* + * Per ES5 15.3.5.2 a user-defined function's .prototype property is + * initially non-configurable, non-enumerable, and writable. Per ES5 13.2 + * the prototype's .constructor property is configurable, non-enumerable, + * and writable. + */ RootedValue protoVal(cx, ObjectValue(*proto)); + RootedValue objVal(cx, ObjectValue(*obj)); if (!JSObject::defineProperty(cx, obj, cx->names().classPrototype, protoVal, JS_PropertyStub, JS_StrictPropertyStub, - JSPROP_PERMANENT)) + JSPROP_PERMANENT) || + !JSObject::defineProperty(cx, proto, cx->names().constructor, + objVal, JS_PropertyStub, JS_StrictPropertyStub, 0)) { - return NULL; - } - - // Per ES5 13.2 the prototype's .constructor property is configurable, - // non-enumerable, and writable. However, per the 15 July 2013 ES6 draft, - // section 15.19.3, the .prototype of a generator function does not link - // back with a .constructor. - if (!isStarGenerator) { - RootedValue objVal(cx, ObjectValue(*obj)); - if (!JSObject::defineProperty(cx, proto, cx->names().constructor, - objVal, JS_PropertyStub, JS_StrictPropertyStub, 0)) - { - return NULL; - } + return NULL; } return proto; @@ -338,15 +326,12 @@ bool js::XDRInterpretedFunction(XDRState *xdr, HandleObject enclosingScope, HandleScript enclosingScript, MutableHandleObject objp) { - enum FirstWordFlag { - HasAtom = 0x1, - IsStarGenerator = 0x2 - }; - /* NB: Keep this in sync with CloneFunctionAndScript. */ RootedAtom atom(xdr->cx()); - uint32_t firstword = 0; /* bitmask of FirstWordFlag */ - uint32_t flagsword = 0; /* word for argument count and fun->flags */ + uint32_t firstword; /* flag telling whether fun->atom is non-null, + plus for fun->u.i.skipmin, fun->u.i.wrapper, + and 14 bits reserved for future use */ + uint32_t flagsword; /* word for argument count and fun->flags */ JSContext *cx = xdr->cx(); RootedFunction fun(cx); @@ -361,38 +346,24 @@ js::XDRInterpretedFunction(XDRState *xdr, HandleObject enclosingScope, Han } return false; } - if (fun->atom()) - firstword |= HasAtom; - if (fun->isStarGenerator()) - firstword |= IsStarGenerator; + firstword = !!fun->atom(); script = fun->getOrCreateScript(cx); if (!script) return false; atom = fun->atom(); flagsword = (fun->nargs << 16) | fun->flags; - - if (!xdr->codeUint32(&firstword)) - return false; } else { - if (!xdr->codeUint32(&firstword)) - return false; - - JSObject *proto = NULL; - if (firstword & IsStarGenerator) { - proto = cx->global()->getOrCreateStarGeneratorFunctionPrototype(cx); - if (!proto) - return false; - } - fun = NewFunctionWithProto(cx, NullPtr(), NULL, 0, JSFunction::INTERPRETED, - NullPtr(), NullPtr(), proto, - JSFunction::FinalizeKind, TenuredObject); + fun = NewFunction(cx, NullPtr(), NULL, 0, JSFunction::INTERPRETED, NullPtr(), NullPtr(), + JSFunction::FinalizeKind, TenuredObject); if (!fun) return false; atom = NULL; script = NULL; } - if ((firstword & HasAtom) && !XDRAtom(xdr, &atom)) + if (!xdr->codeUint32(&firstword)) + return false; + if ((firstword & 1U) && !XDRAtom(xdr, &atom)) return false; if (!xdr->codeUint32(&flagsword)) return false; @@ -427,15 +398,10 @@ JSObject * js::CloneFunctionAndScript(JSContext *cx, HandleObject enclosingScope, HandleFunction srcFun) { /* NB: Keep this in sync with XDRInterpretedFunction. */ - JSObject *cloneProto = NULL; - if (srcFun->isStarGenerator()) { - cloneProto = cx->global()->getOrCreateStarGeneratorFunctionPrototype(cx); - if (!cloneProto) - return NULL; - } - RootedFunction clone(cx, NewFunctionWithProto(cx, NullPtr(), NULL, 0, JSFunction::INTERPRETED, - NullPtr(), NullPtr(), cloneProto, - JSFunction::FinalizeKind, TenuredObject)); + + RootedFunction clone(cx, NewFunction(cx, NullPtr(), NULL, 0, + JSFunction::INTERPRETED, NullPtr(), NullPtr(), + JSFunction::FinalizeKind, TenuredObject)); if (!clone) return NULL; @@ -638,7 +604,7 @@ js::FunctionToString(JSContext *cx, HandleFunction fun, bool bodyOnly, bool lamb return NULL; } if (!fun->isArrow()) { - if (!(fun->isStarGenerator() ? out.append("function* ") : out.append("function "))) + if (!out.append("function ")) return NULL; } if (fun->atom()) { @@ -828,9 +794,6 @@ fun_toSource(JSContext *cx, unsigned argc, Value *vp) if (!obj) return false; - if (!obj->is() && !obj->is()) - return obj_toSource(cx, argc, vp); - RootedString str(cx, fun_toStringHelper(cx, obj, JS_DONT_PRETTY_PRINT)); if (!str) return false; @@ -1363,8 +1326,8 @@ const JSFunctionSpec js::function_methods[] = { JS_FS_END }; -static bool -FunctionConstructor(JSContext *cx, unsigned argc, Value *vp, GeneratorKind generatorKind) +bool +js::Function(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); RootedString arg(cx); // used multiple times below @@ -1381,9 +1344,6 @@ FunctionConstructor(JSContext *cx, unsigned argc, Value *vp, GeneratorKind gener bool hasRest = false; - bool isStarGenerator = generatorKind == StarGenerator; - JS_ASSERT(generatorKind != LegacyGenerator); - const char *filename; unsigned lineno; JSPrincipals *originPrincipals; @@ -1476,7 +1436,6 @@ FunctionConstructor(JSContext *cx, unsigned argc, Value *vp, GeneratorKind gener */ TokenStream ts(cx, options, collected_args.get(), args_length, /* strictModeGetter = */ NULL); - bool yieldIsValidName = ts.versionNumber() < JSVERSION_1_7 && !isStarGenerator; /* The argument string may be empty or contain no tokens. */ TokenKind tt = ts.getToken(); @@ -1491,14 +1450,14 @@ FunctionConstructor(JSContext *cx, unsigned argc, Value *vp, GeneratorKind gener return false; } - if (tt == TOK_YIELD && yieldIsValidName) + if (tt == TOK_YIELD && ts.versionNumber() < JSVERSION_1_7) tt = TOK_NAME; if (tt != TOK_NAME) { if (tt == TOK_TRIPLEDOT) { hasRest = true; tt = ts.getToken(); - if (tt == TOK_YIELD && yieldIsValidName) + if (tt == TOK_YIELD && ts.versionNumber() < JSVERSION_1_7) tt = TOK_NAME; if (tt != TOK_NAME) { if (tt != TOK_ERROR) @@ -1559,47 +1518,24 @@ FunctionConstructor(JSContext *cx, unsigned argc, Value *vp, GeneratorKind gener * and so would a call to f from another top-level's script or function. */ RootedAtom anonymousAtom(cx, cx->names().anonymous); - JSObject *proto = NULL; - if (isStarGenerator) { - proto = global->getOrCreateStarGeneratorFunctionPrototype(cx); - if (!proto) - return false; - } - RootedFunction fun(cx, NewFunctionWithProto(cx, NullPtr(), NULL, 0, - JSFunction::INTERPRETED_LAMBDA, global, - anonymousAtom, proto, - JSFunction::FinalizeKind, TenuredObject)); + RootedFunction fun(cx, NewFunction(cx, NullPtr(), NULL, 0, JSFunction::INTERPRETED_LAMBDA, + global, anonymousAtom, JSFunction::FinalizeKind, + TenuredObject)); if (!fun) return false; if (hasRest) fun->setHasRest(); - bool ok; - if (isStarGenerator) - ok = frontend::CompileStarGeneratorBody(cx, &fun, options, formals, chars, length); - else - ok = frontend::CompileFunctionBody(cx, &fun, options, formals, chars, length); + bool ok = frontend::CompileFunctionBody(cx, &fun, options, formals, chars, length); args.rval().setObject(*fun); return ok; } -bool -js::Function(JSContext *cx, unsigned argc, Value *vp) -{ - return FunctionConstructor(cx, argc, vp, NotGenerator); -} - -bool -js::Generator(JSContext *cx, unsigned argc, Value *vp) -{ - return FunctionConstructor(cx, argc, vp, StarGenerator); -} - bool JSFunction::isBuiltinFunctionConstructor() { - return maybeNative() == Function || maybeNative() == Generator; + return maybeNative() == Function; } JSFunction * @@ -1607,17 +1543,6 @@ js::NewFunction(ExclusiveContext *cx, HandleObject funobjArg, Native native, uns JSFunction::Flags flags, HandleObject parent, HandleAtom atom, gc::AllocKind allocKind /* = JSFunction::FinalizeKind */, NewObjectKind newKind /* = GenericObject */) -{ - return NewFunctionWithProto(cx, funobjArg, native, nargs, flags, parent, atom, NULL, - allocKind, newKind); -} - -JSFunction * -js::NewFunctionWithProto(ExclusiveContext *cx, HandleObject funobjArg, Native native, - unsigned nargs, JSFunction::Flags flags, HandleObject parent, - HandleAtom atom, JSObject *proto, - gc::AllocKind allocKind /* = JSFunction::FinalizeKind */, - NewObjectKind newKind /* = GenericObject */) { JS_ASSERT(allocKind == JSFunction::FinalizeKind || allocKind == JSFunction::ExtendedFinalizeKind); JS_ASSERT(sizeof(JSFunction) <= gc::Arena::thingSize(JSFunction::FinalizeKind)); @@ -1634,7 +1559,7 @@ js::NewFunctionWithProto(ExclusiveContext *cx, HandleObject funobjArg, Native na // that hasSingletonType implies isInterpreted. if (native && !IsAsmJSModuleNative(native)) newKind = SingletonObject; - funobj = NewObjectWithClassProto(cx, &JSFunction::class_, proto, + funobj = NewObjectWithClassProto(cx, &JSFunction::class_, NULL, SkipScopeParent(parent), allocKind, newKind); if (!funobj) return NULL; @@ -1677,13 +1602,7 @@ js::CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent, return NULL; NewObjectKind newKind = useSameScript ? newKindArg : SingletonObject; - JSObject *cloneProto = NULL; - if (fun->isStarGenerator()) { - cloneProto = cx->global()->getOrCreateStarGeneratorFunctionPrototype(cx); - if (!cloneProto) - return NULL; - } - JSObject *cloneobj = NewObjectWithClassProto(cx, &JSFunction::class_, cloneProto, + JSObject *cloneobj = NewObjectWithClassProto(cx, &JSFunction::class_, NULL, SkipScopeParent(parent), allocKind, newKind); if (!cloneobj) return NULL; diff --git a/js/src/jsfun.h b/js/src/jsfun.h index fb37338a9332..1dd31b496e3d 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -416,22 +416,12 @@ namespace js { extern bool Function(JSContext *cx, unsigned argc, Value *vp); -extern bool -Generator(JSContext *cx, unsigned argc, Value *vp); - extern JSFunction * NewFunction(ExclusiveContext *cx, HandleObject funobj, JSNative native, unsigned nargs, JSFunction::Flags flags, HandleObject parent, HandleAtom atom, gc::AllocKind allocKind = JSFunction::FinalizeKind, NewObjectKind newKind = GenericObject); -// If proto is NULL, Function.prototype is used instead. -extern JSFunction * -NewFunctionWithProto(ExclusiveContext *cx, HandleObject funobj, JSNative native, unsigned nargs, - JSFunction::Flags flags, HandleObject parent, HandleAtom atom, - JSObject *proto, gc::AllocKind allocKind = JSFunction::FinalizeKind, - NewObjectKind newKind = GenericObject); - extern JSFunction * DefineFunction(JSContext *cx, HandleObject obj, HandleId id, JSNative native, unsigned nargs, unsigned flags, diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index f9712af0e9f5..f4f8ec349abc 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -958,7 +958,7 @@ const JSFunctionSpec ElementIteratorObject::methods[] = { }; static bool -CloseLegacyGenerator(JSContext *cx, HandleObject genobj); +CloseGenerator(JSContext *cx, HandleObject genobj); bool js::ValueToIterator(JSContext *cx, unsigned flags, MutableHandleValue vp) @@ -999,30 +999,6 @@ js::ValueToIterator(JSContext *cx, unsigned flags, MutableHandleValue vp) return GetIterator(cx, obj, flags, vp); } -static bool -IsGenerator(HandleValue v) -{ - return v.isObject() && v.toObject().is(); -} - -static bool -IsLegacyGenerator(HandleObject obj) -{ - if (!obj->is()) - return false; - JSGenerator *gen = obj->as().getGenerator(); - return gen->regs.fp()->script()->isLegacyGenerator(); -} - -static bool -IsLegacyGenerator(HandleValue v) -{ - if (!IsGenerator(v)) - return false; - JSGenerator *gen = v.toObject().as().getGenerator(); - return gen->regs.fp()->script()->isLegacyGenerator(); -} - bool js::CloseIterator(JSContext *cx, HandleObject obj) { @@ -1044,8 +1020,9 @@ js::CloseIterator(JSContext *cx, HandleObject obj) */ ni->props_cursor = ni->props_array; } - } else if (IsLegacyGenerator(obj)) { - return CloseLegacyGenerator(cx, obj); + } else if (obj->is()) { + // FIXME: Only close legacy generators. + return CloseGenerator(cx, obj); } return true; } @@ -1500,25 +1477,15 @@ js_NewGenerator(JSContext *cx, const FrameRegs &stackRegs) JS_ASSERT(stackfp->script()->isGenerator()); + if (stackfp->script()->isStarGenerator()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_ES6_UNIMPLEMENTED); + return NULL; + } + Rooted global(cx, &stackfp->global()); RootedObject obj(cx); - if (stackfp->script()->isStarGenerator()) { - RootedValue pval(cx); - RootedObject fun(cx, stackfp->fun()); - // FIXME: This would be faster if we could avoid doing a lookup to get - // the prototype for the instance. Bug 906600. - if (!JSObject::getProperty(cx, fun, fun, cx->names().classPrototype, &pval)) - return NULL; - JSObject *proto = pval.isObject() ? &pval.toObject() : NULL; - if (!proto) { - proto = global->getOrCreateStarGeneratorObjectPrototype(cx); - if (!proto) - return NULL; - } - obj = NewObjectWithGivenProto(cx, &GeneratorObject::class_, proto, global); - } else { - JS_ASSERT(stackfp->script()->isLegacyGenerator()); - JSObject *proto = global->getOrCreateLegacyGeneratorObjectPrototype(cx); + { + JSObject *proto = global->getOrCreateGeneratorPrototype(cx); if (!proto) return NULL; obj = NewObjectWithGivenProto(cx, &GeneratorObject::class_, proto, global); @@ -1656,17 +1623,66 @@ SendToGenerator(JSContext *cx, JSGeneratorOp op, HandleObject obj, } static bool -CloseLegacyGenerator(JSContext *cx, HandleObject obj) +CloseGenerator(JSContext *cx, HandleObject obj) { - JS_ASSERT(IsLegacyGenerator(obj)); - JSGenerator *gen = obj->as().getGenerator(); + if (!gen) { + /* Generator prototype object. */ + return true; + } + + // FIXME: Assert that gen is a legacy generator. + if (gen->state == JSGEN_CLOSED) return true; return SendToGenerator(cx, JSGENOP_CLOSE, obj, gen, JS::UndefinedHandleValue); } +JS_ALWAYS_INLINE bool +IsGenerator(HandleValue v) +{ + return v.isObject() && v.toObject().is(); +} + +JS_ALWAYS_INLINE bool +generator_send_impl(JSContext *cx, CallArgs args) +{ + // FIXME: Change assertion to IsLegacyGenerator(). + JS_ASSERT(IsGenerator(args.thisv())); + + RootedObject thisObj(cx, &args.thisv().toObject()); + + JSGenerator *gen = thisObj->as().getGenerator(); + if (!gen || gen->state == JSGEN_CLOSED) { + /* This happens when obj is the generator prototype. See bug 352885. */ + return js_ThrowStopIteration(cx); + } + + if (gen->state == JSGEN_NEWBORN && args.hasDefined(0)) { + RootedValue val(cx, args[0]); + js_ReportValueError(cx, JSMSG_BAD_GENERATOR_SEND, + JSDVG_SEARCH_STACK, val, NullPtr()); + return false; + } + + // FIXME: next() takes the send value as an optional argument in ES6 + // generator objects. + if (!SendToGenerator(cx, JSGENOP_SEND, thisObj, gen, args.get(0))) + return false; + + args.rval().set(gen->fp->returnValue()); + return true; +} + +bool +generator_send(JSContext *cx, unsigned argc, Value *vp) +{ + // FIXME: send() is only a method on legacy generator objects. + CallArgs args = CallArgsFromVp(argc, vp); + return CallNonGenericMethod(cx, args); +} + JS_ALWAYS_INLINE bool generator_next_impl(JSContext *cx, CallArgs args) { @@ -1675,17 +1691,12 @@ generator_next_impl(JSContext *cx, CallArgs args) RootedObject thisObj(cx, &args.thisv().toObject()); JSGenerator *gen = thisObj->as().getGenerator(); - if (gen->state == JSGEN_CLOSED) + if (!gen || gen->state == JSGEN_CLOSED) { + /* This happens when obj is the generator prototype. See bug 352885. */ return js_ThrowStopIteration(cx); - - if (gen->state == JSGEN_NEWBORN && args.hasDefined(0)) { - RootedValue val(cx, args[0]); - js_ReportValueError(cx, JSMSG_BAD_GENERATOR_SEND, - JSDVG_SEARCH_STACK, val, NullPtr()); - return false; } - if (!SendToGenerator(cx, JSGENOP_SEND, thisObj, gen, args.get(0))) + if (!SendToGenerator(cx, JSGENOP_NEXT, thisObj, gen, JS::UndefinedHandleValue)) return false; args.rval().set(gen->fp->returnValue()); @@ -1707,7 +1718,8 @@ generator_throw_impl(JSContext *cx, CallArgs args) RootedObject thisObj(cx, &args.thisv().toObject()); JSGenerator *gen = thisObj->as().getGenerator(); - if (gen->state == JSGEN_CLOSED) { + if (!gen || gen->state == JSGEN_CLOSED) { + /* This happens when obj is the generator prototype. See bug 352885. */ cx->setPendingException(args.length() >= 1 ? args[0] : UndefinedValue()); return false; } @@ -1729,12 +1741,14 @@ generator_throw(JSContext *cx, unsigned argc, Value *vp) JS_ALWAYS_INLINE bool generator_close_impl(JSContext *cx, CallArgs args) { - JS_ASSERT(IsLegacyGenerator(args.thisv())); + // FIXME: Change assertion to IsLegacyGenerator(). + JS_ASSERT(IsGenerator(args.thisv())); RootedObject thisObj(cx, &args.thisv().toObject()); JSGenerator *gen = thisObj->as().getGenerator(); - if (gen->state == JSGEN_CLOSED) { + if (!gen || gen->state == JSGEN_CLOSED) { + /* This happens when obj is the generator prototype. See bug 352885. */ args.rval().setUndefined(); return true; } @@ -1755,47 +1769,22 @@ generator_close_impl(JSContext *cx, CallArgs args) bool generator_close(JSContext *cx, unsigned argc, Value *vp) { + // FIXME: close() is only a method on legacy generator objects. CallArgs args = CallArgsFromVp(argc, vp); - return CallNonGenericMethod(cx, args); + return CallNonGenericMethod(cx, args); } #define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT) -static const JSFunctionSpec legacy_generator_methods[] = { +static const JSFunctionSpec generator_methods[] = { JS_FN("iterator", iterator_iterator, 0, 0), - JS_FN("next", generator_next, 1,JSPROP_ROPERM), - // Send is exactly the same as next. - JS_FN("send", generator_next, 1,JSPROP_ROPERM), + JS_FN("next", generator_next, 0,JSPROP_ROPERM), + JS_FN("send", generator_send, 1,JSPROP_ROPERM), JS_FN("throw", generator_throw, 1,JSPROP_ROPERM), JS_FN("close", generator_close, 0,JSPROP_ROPERM), JS_FS_END }; -static const JSFunctionSpec star_generator_methods[] = { - JS_FN("iterator", iterator_iterator, 0, 0), - JS_FN("next", generator_next, 1,JSPROP_ROPERM), - JS_FN("throw", generator_throw, 1,JSPROP_ROPERM), - JS_FS_END -}; - -static JSObject* -NewObjectWithObjectPrototype(JSContext *cx, Handle global) -{ - JSObject *proto = global->getOrCreateObjectPrototype(cx); - if (!proto) - return NULL; - return NewObjectWithGivenProto(cx, &JSObject::class_, proto, global); -} - -static JSObject* -NewObjectWithFunctionPrototype(JSContext *cx, Handle global) -{ - JSObject *proto = global->getOrCreateFunctionPrototype(cx); - if (!proto) - return NULL; - return NewObjectWithGivenProto(cx, &JSObject::class_, proto, global); -} - /* static */ bool GlobalObject::initIteratorClasses(JSContext *cx, Handle global) { @@ -1837,41 +1826,11 @@ GlobalObject::initIteratorClasses(JSContext *cx, Handle global) global->setReservedSlot(ELEMENT_ITERATOR_PROTO, ObjectValue(*proto)); } - if (global->getSlot(LEGACY_GENERATOR_OBJECT_PROTO).isUndefined()) { - proto = NewObjectWithObjectPrototype(cx, global); - if (!proto || !DefinePropertiesAndBrand(cx, proto, NULL, legacy_generator_methods)) + if (global->getSlot(GENERATOR_PROTO).isUndefined()) { + proto = global->createBlankPrototype(cx, &GeneratorObject::class_); + if (!proto || !DefinePropertiesAndBrand(cx, proto, NULL, generator_methods)) return false; - global->setReservedSlot(LEGACY_GENERATOR_OBJECT_PROTO, ObjectValue(*proto)); - } - - if (global->getSlot(STAR_GENERATOR_OBJECT_PROTO).isUndefined()) { - RootedObject genObjectProto(cx, NewObjectWithObjectPrototype(cx, global)); - if (!genObjectProto) - return false; - if (!DefinePropertiesAndBrand(cx, genObjectProto, NULL, star_generator_methods)) - return false; - - RootedObject genFunctionProto(cx, NewObjectWithFunctionPrototype(cx, global)); - if (!genFunctionProto) - return false; - if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto)) - return false; - - RootedValue function(cx, global->getConstructor(JSProto_Function)); - if (!function.toObjectOrNull()) - return false; - RootedAtom name(cx, cx->names().GeneratorFunction); - RootedObject genFunction(cx, NewFunctionWithProto(cx, NullPtr(), Generator, 1, - JSFunction::NATIVE_CTOR, global, name, - &function.toObject())); - if (!genFunction) - return false; - if (!LinkConstructorAndPrototype(cx, genFunction, genFunctionProto)) - return false; - - global->setSlot(STAR_GENERATOR_OBJECT_PROTO, ObjectValue(*genObjectProto)); - global->setSlot(JSProto_GeneratorFunction, ObjectValue(*genFunction)); - global->setSlot(JSProto_GeneratorFunction + JSProto_LIMIT, ObjectValue(*genFunctionProto)); + global->setReservedSlot(GENERATOR_PROTO, ObjectValue(*proto)); } if (global->getPrototype(JSProto_StopIteration).isUndefined()) { diff --git a/js/src/jsprototypes.h b/js/src/jsprototypes.h index 6f9bbbb3e085..034ff6dd469c 100644 --- a/js/src/jsprototypes.h +++ b/js/src/jsprototypes.h @@ -69,6 +69,5 @@ macro(ArrayType, 50, js_InitBinaryDataClasses) \ macro(StructType, 51, js_InitBinaryDataClasses) \ macro(ArrayTypeObject, 52, js_InitBinaryDataClasses) \ - macro(GeneratorFunction, 53, js_InitIteratorClasses) \ #endif /* jsprototypes_h */ diff --git a/js/src/jsworkers.cpp b/js/src/jsworkers.cpp index 5cdbe1c890e0..8179f3a2f353 100644 --- a/js/src/jsworkers.cpp +++ b/js/src/jsworkers.cpp @@ -236,8 +236,7 @@ js::StartOffThreadParseScript(JSContext *cx, const CompileOptions &options, // pointers can be changed infallibly after parsing finishes. if (!js_GetClassObject(cx, cx->global(), JSProto_Function, &obj) || !js_GetClassObject(cx, cx->global(), JSProto_Array, &obj) || - !js_GetClassObject(cx, cx->global(), JSProto_RegExp, &obj) || - !js_GetClassObject(cx, cx->global(), JSProto_GeneratorFunction, &obj)) + !js_GetClassObject(cx, cx->global(), JSProto_RegExp, &obj)) { return false; } @@ -245,8 +244,7 @@ js::StartOffThreadParseScript(JSContext *cx, const CompileOptions &options, AutoCompartment ac(cx, global); if (!js_GetClassObject(cx, global, JSProto_Function, &obj) || !js_GetClassObject(cx, global, JSProto_Array, &obj) || - !js_GetClassObject(cx, global, JSProto_RegExp, &obj) || - !js_GetClassObject(cx, global, JSProto_GeneratorFunction, &obj)) + !js_GetClassObject(cx, global, JSProto_RegExp, &obj)) { return false; } diff --git a/js/src/tests/ecma_6/Generators/runtime.js b/js/src/tests/ecma_6/Generators/runtime.js deleted file mode 100644 index a55c0d1562c1..000000000000 --- a/js/src/tests/ecma_6/Generators/runtime.js +++ /dev/null @@ -1,150 +0,0 @@ -// This file was written by Andy Wingo and originally -// contributed to V8 as generators-runtime.js, available here: -// -// http://code.google.com/p/v8/source/browse/branches/bleeding_edge/test/mjsunit/harmony/generators-runtime.js - -// Test aspects of the generator runtime. - -// See http://people.mozilla.org/~jorendorff/es6-draft.html#sec-15.19.3. - -function assertSyntaxError(str) { - var msg; - var evil = eval; - try { - // Non-direct eval. - evil(str); - } catch (exc) { - if (exc instanceof SyntaxError) - return; - msg = "Assertion failed: expected SyntaxError, got " + exc; - } - if (msg === undefined) - msg = "Assertion failed: expected SyntaxError, but no exception thrown"; - throw new Error(msg + " - " + str); -} - -function assertFalse(a) { assertEq(a, false) } -function assertTrue(a) { assertEq(a, true) } -function assertNotEq(found, not_expected) { assertFalse(found === expected) } -function assertArrayEq(found, expected) { - assertEq(found.length, expected.length); - for (var i = 0; i < expected.length; i++) - assertEq(found[i], expected[i]); -} - - -function f() { } -function* g() { yield 1; } -var GeneratorFunctionPrototype = Object.getPrototypeOf(g); -var GeneratorFunction = GeneratorFunctionPrototype.constructor; -var GeneratorObjectPrototype = GeneratorFunctionPrototype.prototype; - - -// A generator function should have the same set of properties as any -// other function. -function TestGeneratorFunctionInstance() { - var f_own_property_names = Object.getOwnPropertyNames(f); - var g_own_property_names = Object.getOwnPropertyNames(g); - - f_own_property_names.sort(); - g_own_property_names.sort(); - - assertArrayEq(f_own_property_names, g_own_property_names); - var i; - for (i = 0; i < f_own_property_names.length; i++) { - var prop = f_own_property_names[i]; - var f_desc = Object.getOwnPropertyDescriptor(f, prop); - var g_desc = Object.getOwnPropertyDescriptor(g, prop); - assertEq(f_desc.configurable, g_desc.configurable, prop); - assertEq(f_desc.writable, g_desc.writable, prop); - assertEq(f_desc.enumerable, g_desc.enumerable, prop); - } -} -TestGeneratorFunctionInstance(); - - -// Generators have an additional object interposed in the chain between -// themselves and Function.prototype. -function TestGeneratorFunctionPrototype() { - // Sanity check. - assertEq(Object.getPrototypeOf(f), Function.prototype); - assertNotEq(GeneratorFunctionPrototype, Function.prototype); - assertEq(Object.getPrototypeOf(GeneratorFunctionPrototype), - Function.prototype); - assertEq(Object.getPrototypeOf(function* () {}), - GeneratorFunctionPrototype); -} -TestGeneratorFunctionPrototype(); - - -// Functions that we associate with generator objects are actually defined by -// a common prototype. -function TestGeneratorObjectPrototype() { - assertEq(Object.getPrototypeOf(GeneratorObjectPrototype), - Object.prototype); - assertEq(Object.getPrototypeOf((function*(){yield 1}).prototype), - GeneratorObjectPrototype); - - var expected_property_names = ["iterator", "next", "throw", "constructor"]; - var found_property_names = - Object.getOwnPropertyNames(GeneratorObjectPrototype); - - expected_property_names.sort(); - found_property_names.sort(); - - assertArrayEq(found_property_names, expected_property_names); -} -TestGeneratorObjectPrototype(); - - -// This tests the object that would be called "GeneratorFunction", if it were -// like "Function". -function TestGeneratorFunction() { - assertEq(GeneratorFunctionPrototype, GeneratorFunction.prototype); - assertTrue(g instanceof GeneratorFunction); - - assertEq(Function, Object.getPrototypeOf(GeneratorFunction)); - assertTrue(g instanceof Function); - - assertEq("function* g() { yield 1; }", g.toString()); - - // Not all functions are generators. - assertTrue(f instanceof Function); // Sanity check. - assertFalse(f instanceof GeneratorFunction); - - assertTrue((new GeneratorFunction()) instanceof GeneratorFunction); - assertTrue(GeneratorFunction() instanceof GeneratorFunction); - - assertTrue(GeneratorFunction('yield 1') instanceof GeneratorFunction); - assertTrue(GeneratorFunction('return 1') instanceof GeneratorFunction); - assertTrue(GeneratorFunction('a', 'yield a') instanceof GeneratorFunction); - assertTrue(GeneratorFunction('a', 'return a') instanceof GeneratorFunction); - assertTrue(GeneratorFunction('a', 'return a') instanceof GeneratorFunction); - assertSyntaxError("GeneratorFunction('yield', 'return yield')"); - assertTrue(GeneratorFunction('with (x) return foo;') instanceof GeneratorFunction); - assertSyntaxError("GeneratorFunction('\"use strict\"; with (x) return foo;')"); - - // Doesn't matter particularly what string gets serialized, as long - // as it contains "function*" and "yield 10". - assertEq(GeneratorFunction('yield 10').toString(), - "function* anonymous() {\nyield 10\n}"); -} -TestGeneratorFunction(); - - -function TestPerGeneratorPrototype() { - assertNotEq((function*(){}).prototype, (function*(){}).prototype); - assertNotEq((function*(){}).prototype, g.prototype); - assertEq(typeof GeneratorFunctionPrototype, "object"); - assertEq(g.prototype.__proto__.constructor, GeneratorFunctionPrototype, "object"); - assertEq(Object.getPrototypeOf(g.prototype), GeneratorObjectPrototype); - assertFalse(g.prototype instanceof Function); - assertEq(typeof (g.prototype), "object"); - - assertArrayEq(Object.getOwnPropertyNames(g.prototype), []); -} -TestPerGeneratorPrototype(); - - -if (typeof reportCompare == "function") - reportCompare(true, true); diff --git a/js/src/tests/js1_7/extensions/regress-352885-02.js b/js/src/tests/js1_7/extensions/regress-352885-02.js index 613269bb685f..c6d8bd7805d8 100644 --- a/js/src/tests/js1_7/extensions/regress-352885-02.js +++ b/js/src/tests/js1_7/extensions/regress-352885-02.js @@ -26,35 +26,34 @@ function test() try { proto.next(); - throw "generatorProto.next() does not throw TypeError"; + throw "generatorProto.next() does not throw StopIteration"; } catch (e) { - if (!(e instanceof TypeError)) + if (!(e instanceof StopIteration)) throw "generatorProto.next() throws unexpected exception: "+uneval(e); } try { proto.send(); - throw "generatorProto.send() does not throw TypeError"; + throw "generatorProto.send() does not throw StopIteration"; } catch (e) { - if (!(e instanceof TypeError)) + if (!(e instanceof StopIteration)) throw "generatorProto.send() throws unexpected exception: "+uneval(e); } var obj = {}; try { proto.throw(obj); - throw "generatorProto.throw(obj) does not throw TypeError"; + throw "generatorProto.throw(obj) does not throw obj"; } catch (e) { - if (!(e instanceof TypeError)) + if (e !== obj) throw "generatorProto.throw() throws unexpected exception: "+uneval(e); } + var obj = {}; try { proto.close(); - throw "generatorProto.close() does not throw TypeError"; } catch (e) { - if (!(e instanceof TypeError)) - throw "generatorProto.close() throws unexpected exception: "+uneval(e); + throw "generatorProto.throw() throws exception: "+uneval(e); } } diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h index ead6e4a90a95..a1e948a71efb 100644 --- a/js/src/vm/GlobalObject.h +++ b/js/src/vm/GlobalObject.h @@ -90,9 +90,8 @@ class GlobalObject : public JSObject /* One-off properties stored after slots for built-ins. */ static const unsigned ELEMENT_ITERATOR_PROTO = FROM_BUFFER_UINT8CLAMPED + 1; - static const unsigned LEGACY_GENERATOR_OBJECT_PROTO = ELEMENT_ITERATOR_PROTO + 1; - static const unsigned STAR_GENERATOR_OBJECT_PROTO = LEGACY_GENERATOR_OBJECT_PROTO + 1; - static const unsigned MAP_ITERATOR_PROTO = STAR_GENERATOR_OBJECT_PROTO + 1; + static const unsigned GENERATOR_PROTO = ELEMENT_ITERATOR_PROTO + 1; + static const unsigned MAP_ITERATOR_PROTO = GENERATOR_PROTO + 1; static const unsigned SET_ITERATOR_PROTO = MAP_ITERATOR_PROTO + 1; static const unsigned COLLATOR_PROTO = SET_ITERATOR_PROTO + 1; static const unsigned NUMBER_FORMAT_PROTO = COLLATOR_PROTO + 1; @@ -365,21 +364,8 @@ class GlobalObject : public JSObject return getOrCreateObject(cx, ELEMENT_ITERATOR_PROTO, initIteratorClasses); } - JSObject *getOrCreateLegacyGeneratorObjectPrototype(JSContext *cx) { - return getOrCreateObject(cx, LEGACY_GENERATOR_OBJECT_PROTO, initIteratorClasses); - } - - JSObject *getOrCreateStarGeneratorObjectPrototype(JSContext *cx) { - return getOrCreateObject(cx, STAR_GENERATOR_OBJECT_PROTO, initIteratorClasses); - } - - JSObject *getOrCreateStarGeneratorFunctionPrototype(JSContext *cx) { - return getOrCreateObject(cx, JSProto_LIMIT + JSProto_GeneratorFunction, - initIteratorClasses); - } - - JSObject *getOrCreateStarGeneratorFunction(JSContext *cx) { - return getOrCreateObject(cx, JSProto_GeneratorFunction, initIteratorClasses); + JSObject *getOrCreateGeneratorPrototype(JSContext *cx) { + return getOrCreateObject(cx, GENERATOR_PROTO, initIteratorClasses); } JSObject *getOrCreateMapIteratorPrototype(JSContext *cx) { From 89069fe47ec5150976199867b56be846fd604a8e Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Thu, 22 Aug 2013 16:16:16 +0100 Subject: [PATCH 53/54] Backed out changeset ec184d1877cf (bug 822096) for causing the same B2G failures as occurred in the Try run; CLOSED TREE --- widget/xpwidgets/nsBaseAppShell.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/widget/xpwidgets/nsBaseAppShell.cpp b/widget/xpwidgets/nsBaseAppShell.cpp index e83234febecf..d7573dfc0831 100644 --- a/widget/xpwidgets/nsBaseAppShell.cpp +++ b/widget/xpwidgets/nsBaseAppShell.cpp @@ -181,11 +181,11 @@ nsBaseAppShell::FavorPerformanceHint(bool favorPerfOverStarvation, uint32_t starvationDelay) { mStarvationDelay = PR_MillisecondsToInterval(starvationDelay); - mSwitchTime = PR_IntervalNow(); if (favorPerfOverStarvation) { ++mFavorPerf; } else { --mFavorPerf; + mSwitchTime = PR_IntervalNow(); } return NS_OK; } @@ -268,8 +268,7 @@ nsBaseAppShell::OnProcessNextEvent(nsIThreadInternal *thr, bool mayWait, // NativeEventCallback to process gecko events. mProcessedGeckoEvents = false; - if (mFavorPerf <= 0 || - start > mSwitchTime + std::max(mStarvationDelay, limit)) { + if (mFavorPerf <= 0 && start > mSwitchTime + mStarvationDelay) { // Favor pending native events PRIntervalTime now = start; bool keepGoing; From 05d146d904fbe808de4d38689d6a6d220e8c1a27 Mon Sep 17 00:00:00 2001 From: Jon Coppeard Date: Tue, 20 Aug 2013 16:40:27 +0100 Subject: [PATCH 54/54] Bug 893184 - Call constructors for HeapPtrAtoms allocated as part of shared script data r=till --- js/src/jsscript.cpp | 37 ++++++++++++++++++++++++++++--------- js/src/jsscript.h | 15 ++++++++++----- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 97801aedf378..5f50e79f751e 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -637,7 +637,7 @@ js::XDRScript(XDRState *xdr, HandleObject enclosingScope, HandleScript enc code = ssd->data; if (natoms != 0) { script->natoms = natoms; - script->atoms = ssd->atoms(length, nsrcnotes); + script->atoms = ssd->atoms(); } } @@ -1515,18 +1515,35 @@ SharedScriptData * js::SharedScriptData::new_(ExclusiveContext *cx, uint32_t codeLength, uint32_t srcnotesLength, uint32_t natoms) { + /* + * Ensure the atoms are aligned, as some architectures don't allow unaligned + * access. + */ + const uint32_t pointerSize = sizeof(JSAtom *); + const uint32_t pointerMask = pointerSize - 1; + const uint32_t dataOffset = offsetof(SharedScriptData, data); uint32_t baseLength = codeLength + srcnotesLength; - uint32_t padding = sizeof(JSAtom *) - baseLength % sizeof(JSAtom *); - uint32_t length = baseLength + padding + sizeof(JSAtom *) * natoms; - - SharedScriptData *entry = (SharedScriptData *)cx->malloc_(length + - offsetof(SharedScriptData, data)); + uint32_t padding = (pointerSize - ((baseLength + dataOffset) & pointerMask)) & pointerMask; + uint32_t length = baseLength + padding + pointerSize * natoms; + SharedScriptData *entry = (SharedScriptData *)cx->malloc_(length + dataOffset); if (!entry) return NULL; - entry->marked = false; + entry->length = length; + entry->natoms = natoms; + entry->marked = false; memset(entry->data + baseLength, 0, padding); + + /* + * Call constructors to initialize the storage that will be accessed as a + * HeapPtrAtom array via atoms(). + */ + HeapPtrAtom *atoms = entry->atoms(); + JS_ASSERT(reinterpret_cast(atoms) % sizeof(JSAtom *) == 0); + for (unsigned i = 0; i < natoms; ++i) + new (&atoms[i]) HeapPtrAtom(); + return entry; } @@ -1553,6 +1570,8 @@ SaveSharedScriptData(ExclusiveContext *cx, Handle script, SharedScri ssd = *p; } else { if (!cx->scriptDataTable().add(p, ssd)) { + script->code = NULL; + script->atoms = NULL; js_free(ssd); js_ReportOutOfMemory(cx); return false; @@ -1574,7 +1593,7 @@ SaveSharedScriptData(ExclusiveContext *cx, Handle script, SharedScri #endif script->code = ssd->data; - script->atoms = ssd->atoms(script->length, nsrcnotes); + script->atoms = ssd->atoms(); return true; } @@ -1918,7 +1937,7 @@ JSScript::fullyInitFromEmitter(ExclusiveContext *cx, HandleScript script, Byteco PodCopy(code + prologLength, bce->code().begin(), mainLength); if (!FinishTakingSrcNotes(cx, bce, (jssrcnote *)(code + script->length))) return false; - InitAtomMap(bce->atomIndices.getMap(), ssd->atoms(script->length, nsrcnotes)); + InitAtomMap(bce->atomIndices.getMap(), ssd->atoms()); if (!SaveSharedScriptData(cx, script, ssd, nsrcnotes)) return false; diff --git a/js/src/jsscript.h b/js/src/jsscript.h index a845c52d50c9..ee20caf4239b 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -1427,22 +1427,27 @@ CallDestroyScriptHook(FreeOp *fop, JSScript *script); struct SharedScriptData { - bool marked; uint32_t length; + uint32_t natoms; + bool marked; jsbytecode data[1]; static SharedScriptData *new_(ExclusiveContext *cx, uint32_t codeLength, uint32_t srcnotesLength, uint32_t natoms); - HeapPtrAtom *atoms(uint32_t codeLength, uint32_t srcnotesLength) { - uint32_t length = codeLength + srcnotesLength; - return reinterpret_cast(data + length + sizeof(JSAtom *) - - length % sizeof(JSAtom *)); + HeapPtrAtom *atoms() { + if (!natoms) + return NULL; + return reinterpret_cast(data + length - sizeof(JSAtom *) * natoms); } static SharedScriptData *fromBytecode(const jsbytecode *bytecode) { return (SharedScriptData *)(bytecode - offsetof(SharedScriptData, data)); } + + private: + SharedScriptData() MOZ_DELETE; + SharedScriptData(const SharedScriptData&) MOZ_DELETE; }; struct ScriptBytecodeHasher