diff --git a/browser/base/content/test/Makefile.in b/browser/base/content/test/Makefile.in index d4dcd04986a4..724c6be831dd 100644 --- a/browser/base/content/test/Makefile.in +++ b/browser/base/content/test/Makefile.in @@ -181,6 +181,7 @@ _BROWSER_FILES = \ browser_getshortcutoruri.js \ browser_hide_removing.js \ browser_overflowScroll.js \ + browser_locationBarCommand.js \ browser_locationBarExternalLoad.js \ browser_pageInfo.js \ browser_page_style_menu.js \ diff --git a/browser/base/content/test/browser_bug647886.js b/browser/base/content/test/browser_bug647886.js index 41599ff80e81..4a41fc6ada1c 100644 --- a/browser/base/content/test/browser_bug647886.js +++ b/browser/base/content/test/browser_bug647886.js @@ -4,6 +4,7 @@ function test() { waitForExplicitFinish(); + gBrowser.selectedTab = gBrowser.addTab(); gBrowser.selectedBrowser.addEventListener("load", function () { gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true); @@ -26,6 +27,7 @@ function testBackButton() { ok(true, "history menu opened"); event.target.hidePopup(); + gBrowser.removeTab(gBrowser.selectedTab); finish(); }, false); diff --git a/browser/base/content/test/browser_locationBarCommand.js b/browser/base/content/test/browser_locationBarCommand.js new file mode 100644 index 000000000000..c697e5c1a1a1 --- /dev/null +++ b/browser/base/content/test/browser_locationBarCommand.js @@ -0,0 +1,188 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const TEST_VALUE = "example.com"; +const START_VALUE = "example.org"; + +let gFocusManager = Cc["@mozilla.org/focus-manager;1"]. + getService(Ci.nsIFocusManager); + +function test() { + waitForExplicitFinish(); + runAltLeftClickTest(); +} + +// Monkey patch saveURL to avoid dealing with file save code paths +var oldSaveURL = saveURL; +saveURL = function() { + ok(true, "SaveURL was called"); + is(gURLBar.value, "", "Urlbar reverted to original value"); + saveURL = oldSaveURL; + runShiftLeftClickTest(); +} +function runAltLeftClickTest() { + info("Running test: Alt left click"); + triggerCommand(true, { altKey: true }); +} + +function runShiftLeftClickTest() { + let listener = new WindowListener("chrome://browser/content/browser.xul", function(aWindow) { + Services.wm.removeListener(listener); + addPageShowListener(aWindow.gBrowser, function() { + info("URL should be loaded in a new window"); + is(gURLBar.value, "", "Urlbar reverted to original value"); + is(gFocusManager.focusedElement, null, "There should be no focused element"); + is(gFocusManager.focusedWindow, aWindow.gBrowser.contentWindow, "Content window should be focused"); + is(aWindow.gURLBar.value, TEST_VALUE, "New URL is loaded in new window"); + + aWindow.close(); + runNextTest(); + }); + }); + Services.wm.addListener(listener); + + info("Running test: Shift left click"); + triggerCommand(true, { shiftKey: true }); +} + +function runNextTest() { + let test = gTests.shift(); + if (!test) { + finish(); + return; + } + + info("Running test: " + test.desc); + // Tab will be blank if test.startValue is null + let tab = gBrowser.selectedTab = gBrowser.addTab(test.startValue); + addPageShowListener(gBrowser, function() { + triggerCommand(test.click, test.event); + test.check(tab); + + // Clean up + while (gBrowser.tabs.length > 1) + gBrowser.removeTab(gBrowser.selectedTab) + runNextTest(); + }); +} + +let gTests = [ + { desc: "Right click on go button", + click: true, + event: { button: 2 }, + check: function(aTab) { + // Right click should do nothing (context menu will be shown) + is(gURLBar.value, TEST_VALUE, "Urlbar still has the value we entered"); + ok(gURLBar.focused, "Urlbar is still focused after click"); + } + }, + + { desc: "Left click on go button", + click: true, + event: {}, + check: checkCurrent + }, + + { desc: "Ctrl/Cmd left click on go button", + click: true, + event: { accelKey: true }, + check: checkNewTab + }, + + { desc: "Shift+Ctrl/Cmd left click on go button", + click: true, + event: { accelKey: true, shiftKey: true }, + check: function(aTab) { + info("URL should be loaded in a new background tab"); + is(gURLBar.value, "", "Urlbar reverted to original value"); + ok(!gURLBar.focused, "Urlbar is no longer focused after urlbar command"); + is(gBrowser.selectedTab, aTab, "Focus did not change to the new tab"); + + // Select the new background tab + gBrowser.selectedTab = gBrowser.selectedTab.nextSibling; + is(gURLBar.value, TEST_VALUE, "New URL is loaded in new tab"); + } + }, + + { desc: "Simple return keypress", + event: {}, + check: checkCurrent + }, + + { desc: "Alt+Return keypress in a blank tab", + event: { altKey: true }, + check: checkCurrent + }, + + { desc: "Alt+Return keypress in a dirty tab", + event: { altKey: true }, + check: checkNewTab, + startValue: START_VALUE + }, + + { desc: "Ctrl/Cmd+Return keypress", + event: { accelKey: true }, + check: checkCurrent + } +] + +let gGoButton = document.getElementById("urlbar-go-button"); +function triggerCommand(aClick, aEvent) { + gURLBar.value = TEST_VALUE; + gURLBar.focus(); + + if (aClick) + EventUtils.synthesizeMouseAtCenter(gGoButton, aEvent); + else + EventUtils.synthesizeKey("VK_RETURN", aEvent); +} + +/* Checks that the URL was loaded in the current tab */ +function checkCurrent(aTab) { + info("URL should be loaded in the current tab"); + is(gURLBar.value, TEST_VALUE, "Urlbar still has the value we entered"); + is(gFocusManager.focusedElement, null, "There should be no focused element"); + is(gFocusManager.focusedWindow, gBrowser.contentWindow, "Content window should be focused"); + is(gBrowser.selectedTab, aTab, "New URL was loaded in the current tab"); +} + +/* Checks that the URL was loaded in a new focused tab */ +function checkNewTab(aTab) { + info("URL should be loaded in a new focused tab"); + is(gURLBar.value, TEST_VALUE, "Urlbar still has the value we entered"); + is(gFocusManager.focusedElement, null, "There should be no focused element"); + is(gFocusManager.focusedWindow, gBrowser.contentWindow, "Content window should be focused"); + isnot(gBrowser.selectedTab, aTab, "New URL was loaded in a new tab"); +} + +function addPageShowListener(aBrowser, aFunc) { + aBrowser.selectedBrowser.addEventListener("pageshow", function loadListener() { + aBrowser.selectedBrowser.removeEventListener("pageshow", loadListener, false); + aFunc(); + }); +} + +function WindowListener(aURL, aCallback) { + this.callback = aCallback; + this.url = aURL; +} +WindowListener.prototype = { + onOpenWindow: function(aXULWindow) { + var domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow); + var self = this; + domwindow.addEventListener("load", function() { + domwindow.removeEventListener("load", arguments.callee, false); + + if (domwindow.document.location.href != self.url) + return; + + // Allow other window load listeners to execute before passing to callback + executeSoon(function() { + self.callback(domwindow); + }); + }, false); + }, + onCloseWindow: function(aXULWindow) {}, + onWindowTitleChange: function(aXULWindow, aNewTitle) {} +} diff --git a/browser/base/content/urlbarBindings.xml b/browser/base/content/urlbarBindings.xml index 6ea464fdca62..aaa9c62d7ce4 100644 --- a/browser/base/content/urlbarBindings.xml +++ b/browser/base/content/urlbarBindings.xml @@ -334,26 +334,35 @@ // area when the current tab is re-selected. gBrowser.selectedBrowser.focus(); - if (aTriggeringEvent instanceof MouseEvent) { - // We have a mouse event (from the go button), so use the standard - // UI link behaviors - let where = whereToOpenLink(aTriggeringEvent, false, false); + let isMouseEvent = aTriggeringEvent instanceof MouseEvent; + let altEnter = !isMouseEvent && aTriggeringEvent && aTriggeringEvent.altKey; + + if (altEnter) { + // XXX This was added a long time ago, and I'm not sure why it is + // necessary. Alt+Enter's default action might cause a system beep, + // or something like that? + aTriggeringEvent.preventDefault(); + aTriggeringEvent.stopPropagation(); + } + + // If the current tab is empty, ignore Alt+Enter (just reuse this tab) + altEnter = altEnter && !isTabEmpty(gBrowser.selectedTab); + + if (isMouseEvent || altEnter) { + // Use the standard UI link behaviors for clicks or Alt+Enter + let where = "tab"; + if (isMouseEvent) + where = whereToOpenLink(aTriggeringEvent, false, false); + if (where == "current") { loadCurrent(); } else { this.handleRevert(); - openUILinkIn(url, where, - { allowThirdPartyFixup: true, postData: postData }); + let params = { allowThirdPartyFixup: true, postData: postData }; + if (altEnter) + params.inBackground = false; + openUILinkIn(url, where, params); } - } else if (aTriggeringEvent && aTriggeringEvent.altKey && - !isTabEmpty(gBrowser.selectedTab)) { - this.handleRevert(); - gBrowser.loadOneTab(url, { - postData: postData, - inBackground: false, - allowThirdPartyFixup: true}); - aTriggeringEvent.preventDefault(); - aTriggeringEvent.stopPropagation(); } else { loadCurrent(); } diff --git a/browser/base/content/utilityOverlay.js b/browser/base/content/utilityOverlay.js index 85f84d1b75a5..778d71d9a00f 100644 --- a/browser/base/content/utilityOverlay.js +++ b/browser/base/content/utilityOverlay.js @@ -195,6 +195,7 @@ function openLinkIn(url, where, params) { var aCharset = params.charset; var aReferrerURI = params.referrerURI; var aRelatedToCurrent = params.relatedToCurrent; + var aInBackground = params.inBackground; if (where == "save") { saveURL(url, null, null, true, null, aReferrerURI); @@ -240,9 +241,12 @@ function openLinkIn(url, where, params) { return; } - var loadInBackground = aFromChrome ? + let loadInBackground = aInBackground; + if (loadInBackground == null) { + loadInBackground = aFromChrome ? getBoolPref("browser.tabs.loadBookmarksInBackground") : getBoolPref("browser.tabs.loadInBackground"); + } if (where == "current" && w.gBrowser.selectedTab.pinned) { try { diff --git a/browser/components/Makefile.in b/browser/components/Makefile.in index f1b9d587fb59..4c2a3a296a95 100644 --- a/browser/components/Makefile.in +++ b/browser/components/Makefile.in @@ -69,7 +69,7 @@ PARALLEL_DIRS = \ search \ sessionstore \ shell \ - sidebar \ + sidebar/src \ migration \ $(NULL) @@ -82,7 +82,7 @@ PARALLEL_DIRS += safebrowsing endif ifdef ENABLE_TESTS -DIRS += test +DIRS += test/browser endif DIRS += build diff --git a/browser/components/preferences/aboutPermissions.js b/browser/components/preferences/aboutPermissions.js index e37eb0ee905d..1b4ade90ca1c 100644 --- a/browser/components/preferences/aboutPermissions.js +++ b/browser/components/preferences/aboutPermissions.js @@ -365,6 +365,12 @@ let AboutPermissions = { */ PLACES_SITES_LIMIT: 50, + /** + * When adding sites to the dom sites-list, divide workload into intervals. + */ + LIST_BUILD_CHUNK: 5, // interval size + LIST_BUILD_DELAY: 100, // delay between intervals + /** * Stores a mapping of host strings to Site objects. */ @@ -373,6 +379,12 @@ let AboutPermissions = { sitesList: null, _selectedSite: null, + /** + * For testing, track initializations so we can send notifications + */ + _initPlacesDone: false, + _initServicesDone: false, + /** * This reflects the permissions that we expose in the UI. These correspond * to permission type strings in the permission manager, PermissionDefaults, @@ -397,7 +409,9 @@ let AboutPermissions = { this.sitesList = document.getElementById("sites-list"); this.getSitesFromPlaces(); - this.enumerateServices(); + + this.enumerateServicesGenerator = this.getEnumerateServicesGenerator(); + setTimeout(this.enumerateServicesDriver.bind(this), this.LIST_BUILD_DELAY); // Attach observers in case data changes while the page is open. Services.prefs.addObserver("signon.rememberSignons", this, false); @@ -412,6 +426,7 @@ let AboutPermissions = { Services.obs.addObserver(this, "browser:purge-domain-data", false); this._observersInitialized = true; + Services.obs.notifyObservers(null, "browser-permissions-preinit", null); }, /** @@ -501,20 +516,43 @@ let AboutPermissions = { }, handleCompletion: function(aReason) { // Notify oberservers for testing purposes. - Services.obs.notifyObservers(null, "browser-permissions-initialized", null); + AboutPermissions._initPlacesDone = true; + if (AboutPermissions._initServicesDone) { + Services.obs.notifyObservers(null, "browser-permissions-initialized", null); + } } }); }, + /** + * Drives getEnumerateServicesGenerator to work in intervals. + */ + enumerateServicesDriver: function() { + if (this.enumerateServicesGenerator.next()) { + // Build top sitesList items faster so that the list never seems sparse + let delay = Math.min(this.sitesList.itemCount * 5, this.LIST_BUILD_DELAY); + setTimeout(this.enumerateServicesDriver.bind(this), delay); + } else { + this.enumerateServicesGenerator.close(); + this._initServicesDone = true; + if (this._initPlacesDone) { + Services.obs.notifyObservers(null, "browser-permissions-initialized", null); + } + } + }, + /** * Finds sites that have non-default permissions and creates Site objects for * them if they are not already stored in _sites. */ - enumerateServices: function() { - this.startSitesListBatch(); + getEnumerateServicesGenerator: function() { + let itemCnt = 1; let logins = Services.logins.getAllLogins(); logins.forEach(function(aLogin) { + if (itemCnt % this.LIST_BUILD_CHUNK == 0) { + yield true; + } try { // aLogin.hostname is a string in origin URL format (e.g. "http://foo.com") let uri = NetUtil.newURI(aLogin.hostname); @@ -522,10 +560,14 @@ let AboutPermissions = { } catch (e) { // newURI will throw for add-ons logins stored in chrome:// URIs } + itemCnt++; }, this); let disabledHosts = Services.logins.getAllDisabledHosts(); disabledHosts.forEach(function(aHostname) { + if (itemCnt % this.LIST_BUILD_CHUNK == 0) { + yield true; + } try { // aHostname is a string in origin URL format (e.g. "http://foo.com") let uri = NetUtil.newURI(aHostname); @@ -533,19 +575,24 @@ let AboutPermissions = { } catch (e) { // newURI will throw for add-ons logins stored in chrome:// URIs } + itemCnt++; }, this); let (enumerator = Services.perms.enumerator) { while (enumerator.hasMoreElements()) { + if (itemCnt % this.LIST_BUILD_CHUNK == 0) { + yield true; + } let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission); // Only include sites with exceptions set for supported permission types. if (this._supportedPermissions.indexOf(permission.type) != -1) { this.addHost(permission.host); } + itemCnt++; } } - this.endSitesListBatch(); + yield false; }, /** @@ -579,7 +626,11 @@ let AboutPermissions = { }); aSite.listitem = item; - (this._listFragment || this.sitesList).appendChild(item); + // Make sure to only display relevant items when list is filtered + let filterValue = document.getElementById("sites-filter").value.toLowerCase(); + item.collapsed = aSite.host.toLowerCase().indexOf(filterValue) == -1; + + this.sitesList.appendChild(item); }, startSitesListBatch: function () { @@ -640,7 +691,7 @@ let AboutPermissions = { this.sitesList.removeChild(site.listitem); delete this._sites[site.host]; } - } + } }, /** diff --git a/browser/components/preferences/tests/Makefile.in b/browser/components/preferences/tests/Makefile.in index 2f4f12cc163b..5c148b375114 100644 --- a/browser/components/preferences/tests/Makefile.in +++ b/browser/components/preferences/tests/Makefile.in @@ -56,6 +56,7 @@ _BROWSER_FILES = \ browser_privacypane_7.js \ browser_privacypane_8.js \ browser_permissions.js \ + browser_chunk_permissions.js \ $(NULL) libs:: $(_BROWSER_FILES) diff --git a/browser/components/preferences/tests/browser_chunk_permissions.js b/browser/components/preferences/tests/browser_chunk_permissions.js new file mode 100644 index 000000000000..aeb1f98db968 --- /dev/null +++ b/browser/components/preferences/tests/browser_chunk_permissions.js @@ -0,0 +1,151 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +Components.utils.import("resource://gre/modules/PlacesUtils.jsm"); +Components.utils.import("resource://gre/modules/NetUtil.jsm"); + +const ABOUT_PERMISSIONS_SPEC = "about:permissions"; + +const TEST_URI_1 = NetUtil.newURI("http://mozilla.com/"); +const TEST_URI_2 = NetUtil.newURI("http://mozilla.org/"); +const TEST_URI_3 = NetUtil.newURI("http://wikipedia.org/"); + +// values from DefaultPermissions object +const PERM_UNKNOWN = 0; +const PERM_ALLOW = 1; +const PERM_DENY = 2; +const PERM_SESION = 8; + +// used to set permissions on test sites +const TEST_PERMS = { + "password": PERM_ALLOW, + "cookie": PERM_ALLOW, + "geo": PERM_UNKNOWN, + "indexedDB": PERM_UNKNOWN, + "popup": PERM_DENY +}; + +function test() { + waitForExplicitFinish(); + registerCleanupFunction(cleanUp); + setup(); + runNextTest(); +} + +function setup() { + // add test history visit + PlacesUtils.history.addVisit(TEST_URI_1, Date.now() * 1000, null, + Ci.nsINavHistoryService.TRANSITION_LINK, false, 0); + + // set permissions ourselves to avoid problems with different defaults + // from test harness configuration + for (let type in TEST_PERMS) { + if (type == "password") { + Services.logins.setLoginSavingEnabled(TEST_URI_2.prePath, true); + } else { + // set permissions on a site without history visits to test enumerateServices + Services.perms.add(TEST_URI_2, type, TEST_PERMS[type]); + } + } + + Services.perms.add(TEST_URI_3, "popup", TEST_PERMS["popup"]); +} + +function cleanUp() { + for (let type in TEST_PERMS) { + if (type != "password") { + Services.perms.remove(TEST_URI_1.host, type); + Services.perms.remove(TEST_URI_2.host, type); + Services.perms.remove(TEST_URI_3.host, type); + } + } +} + +function runNextTest() { + if (gTestIndex == tests.length) { + waitForClearHistory(finish); + return; + } + + let nextTest = tests[gTestIndex++]; + info(nextTest.desc); + + function preinit_observer() { + Services.obs.removeObserver(preinit_observer, "browser-permissions-preinit", false); + nextTest.preInit(); + } + Services.obs.addObserver(preinit_observer, "browser-permissions-preinit", false); + + function init_observer() { + Services.obs.removeObserver(init_observer, "browser-permissions-initialized", false); + nextTest.run(); + } + Services.obs.addObserver(init_observer, "browser-permissions-initialized", false); + + // open about:permissions + let tab = gBrowser.selectedTab = gBrowser.addTab("about:permissions"); + registerCleanupFunction(function() { + gBrowser.removeTab(tab); + }); +} + +var gSitesList; + +var gTestIndex = 0; +var tests = [ + // 'preInit' occurs after opening about:permissions, before sites-list is populated + // 'run' occurs after sites-list is populated + { + desc: "test filtering before sites-list is fully constructed.", + preInit: function() { + let sitesFilter = gBrowser.contentDocument.getElementById("sites-filter"); + sitesFilter.value = TEST_URI_2.host; + sitesFilter.doCommand(); + }, + run: function() { + let testSite1 = getSiteItem(TEST_URI_1.host); + ok(testSite1.collapsed, "test site 1 is collapsed after early filtering"); + let testSite2 = getSiteItem(TEST_URI_2.host); + ok(!testSite2.collapsed, "test site 2 is not collapsed after early filtering"); + let testSite3 = getSiteItem(TEST_URI_3.host); + ok(testSite3.collapsed, "test site 3 is collapsed after early filtering"); + + runNextTest(); + } + }, + { + desc: "test removing from sites-list before it is fully constructed.", + preInit: function() { + let pb = Cc["@mozilla.org/privatebrowsing;1"]. + getService(Ci.nsIPrivateBrowsingService); + pb.removeDataFromDomain(TEST_URI_2.host); + }, + run: function() { + let testSite1 = getSiteItem(TEST_URI_1.host); + ok(!testSite2, "test site 1 was not removed from sites list"); + let testSite2 = getSiteItem(TEST_URI_2.host); + ok(!testSite2, "test site 2 was pre-removed from sites list"); + let testSite3 = getSiteItem(TEST_URI_3.host); + ok(!testSite2, "test site 3 was not removed from sites list"); + + runNextTest(); + } + } +]; + +function getSiteItem(aHost) { + return gBrowser.contentDocument. + querySelector(".site[value='" + aHost + "']"); +} + +// copied from toolkit/components/places/tests/head_common.js +function waitForClearHistory(aCallback) { + let observer = { + observe: function(aSubject, aTopic, aData) { + Services.obs.removeObserver(this, PlacesUtils.TOPIC_EXPIRATION_FINISHED); + aCallback(); + } + }; + Services.obs.addObserver(observer, PlacesUtils.TOPIC_EXPIRATION_FINISHED, false); + PlacesUtils.bhistory.removeAllPages(); +} diff --git a/browser/components/safebrowsing/Makefile.in b/browser/components/safebrowsing/Makefile.in index 9592aa30bee0..2d526c2933cc 100644 --- a/browser/components/safebrowsing/Makefile.in +++ b/browser/components/safebrowsing/Makefile.in @@ -43,8 +43,6 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk -DIRS = src - ifdef ENABLE_TESTS DIRS += content/test endif diff --git a/browser/components/safebrowsing/src/Makefile.in b/browser/components/safebrowsing/src/Makefile.in deleted file mode 100644 index 2339762f5a9c..000000000000 --- a/browser/components/safebrowsing/src/Makefile.in +++ /dev/null @@ -1,44 +0,0 @@ -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is Google Inc. -# Portions created by the Initial Developer are Copyright (C) 2006 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - - -DEPTH = ../../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -include $(topsrcdir)/config/rules.mk diff --git a/browser/components/sessionstore/Makefile.in b/browser/components/sessionstore/Makefile.in index 81468ad0d42a..8d69860a26d4 100644 --- a/browser/components/sessionstore/Makefile.in +++ b/browser/components/sessionstore/Makefile.in @@ -51,7 +51,7 @@ XPIDLSRCS = \ DIRS = src ifdef ENABLE_TESTS -DIRS += test +DIRS += test/browser endif include $(topsrcdir)/config/rules.mk diff --git a/browser/components/sessionstore/test/Makefile.in b/browser/components/sessionstore/test/Makefile.in deleted file mode 100644 index 9330df3c2f45..000000000000 --- a/browser/components/sessionstore/test/Makefile.in +++ /dev/null @@ -1,49 +0,0 @@ -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Mozilla Foundation. -# Portions created by the Initial Developer are Copyright (C) 2007 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - - -DIRS += browser \ - $(NULL) - -include $(topsrcdir)/config/rules.mk - diff --git a/browser/components/sidebar/Makefile.in b/browser/components/sidebar/Makefile.in deleted file mode 100644 index a0fd8a176ebd..000000000000 --- a/browser/components/sidebar/Makefile.in +++ /dev/null @@ -1,48 +0,0 @@ -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is mozilla.org Code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1999 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -DIRS = src - -include $(topsrcdir)/config/rules.mk - diff --git a/browser/components/test/Makefile.in b/browser/components/test/Makefile.in deleted file mode 100644 index 6c606ca8e782..000000000000 --- a/browser/components/test/Makefile.in +++ /dev/null @@ -1,48 +0,0 @@ -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# the Mozilla Foundation. -# Portions created by the Initial Developer are Copyright (C) 2010 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Robert Strong (Original Author) -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -DIRS = browser - -include $(topsrcdir)/config/rules.mk diff --git a/browser/devtools/Makefile.in b/browser/devtools/Makefile.in index 82d739894f83..7b9ab208da68 100644 --- a/browser/devtools/Makefile.in +++ b/browser/devtools/Makefile.in @@ -49,14 +49,13 @@ include $(topsrcdir)/config/config.mk DIRS = \ highlighter \ webconsole \ - scratchpad \ sourceeditor \ styleinspector \ shared \ $(NULL) ifdef ENABLE_TESTS -# DIRS += test # no tests yet +DIRS += scratchpad/test endif include $(topsrcdir)/config/rules.mk diff --git a/browser/devtools/scratchpad/Makefile.in b/browser/devtools/scratchpad/Makefile.in deleted file mode 100644 index bf57db4e4f2f..000000000000 --- a/browser/devtools/scratchpad/Makefile.in +++ /dev/null @@ -1,50 +0,0 @@ -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Scratchpad Build Code. -# -# The Initial Developer of the Original Code is The Mozilla Foundation. -# -# Portions created by the Initial Developer are Copyright (C) 2011 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Rob Campbell -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -ifdef ENABLE_TESTS - DIRS += test -endif - -include $(topsrcdir)/config/rules.mk diff --git a/browser/devtools/styleinspector/Makefile.in b/browser/devtools/styleinspector/Makefile.in index 28dddd962d78..626dab7d87fc 100644 --- a/browser/devtools/styleinspector/Makefile.in +++ b/browser/devtools/styleinspector/Makefile.in @@ -45,11 +45,11 @@ include $(DEPTH)/config/autoconf.mk ifdef ENABLE_TESTS ifneq (mobile,$(MOZ_BUILD_APP)) - DIRS += test +DIRS += test/browser endif endif include $(topsrcdir)/config/rules.mk libs:: - $(NSINSTALL) $(srcdir)/*.jsm $(FINAL_TARGET)/modules/devtools \ No newline at end of file + $(NSINSTALL) $(srcdir)/*.jsm $(FINAL_TARGET)/modules/devtools diff --git a/browser/devtools/styleinspector/test/Makefile.in b/browser/devtools/styleinspector/test/Makefile.in deleted file mode 100644 index b96d4dd7433a..000000000000 --- a/browser/devtools/styleinspector/test/Makefile.in +++ /dev/null @@ -1,48 +0,0 @@ -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Style Inspector code. -# -# The Initial Developer of the Original Code is -# Mozilla Corporation. -# Portions created by the Initial Developer are Copyright (C) 2010 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mike Ratcliffe (Original author) -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -DIRS = browser - -include $(topsrcdir)/config/rules.mk diff --git a/browser/devtools/webconsole/Makefile.in b/browser/devtools/webconsole/Makefile.in index ef5c20633f8c..a31a7d9e038e 100644 --- a/browser/devtools/webconsole/Makefile.in +++ b/browser/devtools/webconsole/Makefile.in @@ -57,7 +57,7 @@ EXTRA_PP_JS_MODULES = \ ifdef ENABLE_TESTS ifneq (mobile,$(MOZ_BUILD_APP)) - DIRS += test +DIRS += test/browser endif endif diff --git a/browser/devtools/webconsole/test/Makefile.in b/browser/devtools/webconsole/test/Makefile.in deleted file mode 100644 index 0e1278a64692..000000000000 --- a/browser/devtools/webconsole/test/Makefile.in +++ /dev/null @@ -1,48 +0,0 @@ -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is HUDService code. -# -# The Initial Developer of the Original Code is -# Mozilla Corporation. -# Portions created by the Initial Developer are Copyright (C) 2010 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# David Dahl (Original author) -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -DIRS = browser - -include $(topsrcdir)/config/rules.mk diff --git a/browser/makefiles.sh b/browser/makefiles.sh index f102ed7503be..ed72d3dd446b 100644 --- a/browser/makefiles.sh +++ b/browser/makefiles.sh @@ -56,11 +56,9 @@ browser/components/preferences/Makefile browser/components/privatebrowsing/Makefile browser/components/privatebrowsing/src/Makefile browser/components/safebrowsing/Makefile -browser/components/safebrowsing/src/Makefile browser/components/search/Makefile browser/components/sessionstore/Makefile browser/components/sessionstore/src/Makefile -browser/components/sidebar/Makefile browser/components/sidebar/src/Makefile browser/components/shell/Makefile browser/components/shell/public/Makefile @@ -90,7 +88,6 @@ if [ "$ENABLE_TESTS" ]; then browser/components/certerror/test/Makefile browser/components/preferences/tests/Makefile browser/components/search/test/Makefile - browser/components/sessionstore/test/Makefile browser/components/sessionstore/test/browser/Makefile browser/components/shell/test/Makefile browser/components/feeds/test/Makefile diff --git a/build/mobile/devicemanagerSUT.py b/build/mobile/devicemanagerSUT.py index c8340655a05a..fffe65abec3f 100644 --- a/build/mobile/devicemanagerSUT.py +++ b/build/mobile/devicemanagerSUT.py @@ -684,7 +684,7 @@ class DeviceManagerSUT(DeviceManager): return None # prompt should follow read_exact(len(prompt), buffer, 'could not find prompt') - print 'DeviceManager: error pulling file: %s' % error_str + print "DeviceManager: error pulling file '%s': %s" % (remoteFile, error_str) return None # read file data diff --git a/build/mobile/remoteautomation.py b/build/mobile/remoteautomation.py index de38525ecff7..d1d321aa7283 100644 --- a/build/mobile/remoteautomation.py +++ b/build/mobile/remoteautomation.py @@ -40,6 +40,9 @@ import time import sys import os import socket +import automationutils +import tempfile +import shutil from automation import Automation from devicemanager import DeviceManager, NetworkTools @@ -105,6 +108,15 @@ class RemoteAutomation(Automation): return status + def checkForCrashes(self, directory, symbolsPath): + dumpDir = tempfile.mkdtemp() + self._devicemanager.getDirectory(self._remoteProfile + '/minidumps/', dumpDir) + automationutils.checkForCrashes(dumpDir, symbolsPath, self.lastTestSeen) + try: + shutil.rmtree(dumpDir) + except: + print "WARNING: unable to remove directory: %s" % (dumpDir) + def buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs): # If remote profile is specified, use that instead if (self._remoteProfile): diff --git a/caps/Makefile.in b/caps/Makefile.in index e5641d90ee2f..b89c840681c6 100644 --- a/caps/Makefile.in +++ b/caps/Makefile.in @@ -46,7 +46,7 @@ MODULE = caps DIRS = idl include src ifdef ENABLE_TESTS -DIRS += tests +DIRS += tests/mochitest endif include $(topsrcdir)/config/rules.mk diff --git a/caps/tests/Makefile.in b/caps/tests/Makefile.in deleted file mode 100644 index f78ff7f45041..000000000000 --- a/caps/tests/Makefile.in +++ /dev/null @@ -1,48 +0,0 @@ -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Mozilla Foundation. -# Portions created by the Initial Developer are Copyright (C) 2008 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mike Shaver (original cutter and paster) -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -DIRS = mochitest - -include $(topsrcdir)/config/rules.mk diff --git a/content/Makefile.in b/content/Makefile.in index f57abdcf4bc2..4d1661b9d923 100644 --- a/content/Makefile.in +++ b/content/Makefile.in @@ -43,7 +43,7 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk MODULE = content -PARALLEL_DIRS = base canvas events html mathml smil svg xml xul xbl xslt +PARALLEL_DIRS = base canvas events html mathml/content/src smil svg xml xul xbl xslt ifdef MOZ_MEDIA PARALLEL_DIRS += media diff --git a/content/events/src/nsEventListenerManager.cpp b/content/events/src/nsEventListenerManager.cpp index a0ea82cd3ccf..7d634af9c082 100644 --- a/content/events/src/nsEventListenerManager.cpp +++ b/content/events/src/nsEventListenerManager.cpp @@ -333,6 +333,11 @@ nsEventListenerManager::RemoveEventListener(nsIDOMEventListener *aListener, mListeners.RemoveElementAt(i); mNoListenerForEvent = NS_EVENT_TYPE_NULL; mNoListenerForEventAtom = nsnull; + if (aType == NS_DEVICE_ORIENTATION) { + nsPIDOMWindow* window = GetInnerWindowForTarget(); + if (window) + window->RemoveOrientationEventListener(); + } break; } } diff --git a/content/events/test/test_bug648573.html b/content/events/test/test_bug648573.html index 1b3f4a9943a8..85043b34a272 100644 --- a/content/events/test/test_bug648573.html +++ b/content/events/test/test_bug648573.html @@ -1,7 +1,7 @@ Test for Bug 648573 @@ -19,7 +19,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=648573 /** Test for Bug 648573 **/ -ok(!SpecialPowers.DOMWindowUtils.mayHaveTouchEventListeners, +var utils = SpecialPowers.getDOMWindowUtils(window); + +ok(!utils.mayHaveTouchEventListeners, "There shouldn't be any touch event listeners yet."); ok("createTouch" in document, "Should have createTouch function"); @@ -101,7 +103,7 @@ for (var i = 0; i < events.length; ++i) { runEventTest(events[i]); } -ok(SpecialPowers.DOMWindowUtils.mayHaveTouchEventListeners, +ok(utils.mayHaveTouchEventListeners, "There should be touch event listeners."); diff --git a/content/events/test/test_bug662678.html b/content/events/test/test_bug662678.html index 334baf9bf631..dfcf5dcf8452 100644 --- a/content/events/test/test_bug662678.html +++ b/content/events/test/test_bug662678.html @@ -18,8 +18,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=662678 diff --git a/content/mathml/Makefile.in b/content/mathml/Makefile.in deleted file mode 100644 index 71838f2e6886..000000000000 --- a/content/mathml/Makefile.in +++ /dev/null @@ -1,47 +0,0 @@ -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -DIRS = content - -include $(topsrcdir)/config/rules.mk diff --git a/content/mathml/content/Makefile.in b/content/mathml/content/Makefile.in deleted file mode 100644 index ac483e08e956..000000000000 --- a/content/mathml/content/Makefile.in +++ /dev/null @@ -1,48 +0,0 @@ -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -DIRS = src - -include $(topsrcdir)/config/rules.mk - diff --git a/content/svg/Makefile.in b/content/svg/Makefile.in index 5ae8366fab3d..37ed9c9f89fb 100644 --- a/content/svg/Makefile.in +++ b/content/svg/Makefile.in @@ -42,6 +42,6 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk -PARALLEL_DIRS = document content +PARALLEL_DIRS = document/src content include $(topsrcdir)/config/rules.mk diff --git a/content/svg/content/src/nsSVGString.cpp b/content/svg/content/src/nsSVGString.cpp index a4c20429d043..b0b7f484ea94 100644 --- a/content/svg/content/src/nsSVGString.cpp +++ b/content/svg/content/src/nsSVGString.cpp @@ -64,8 +64,8 @@ nsSVGString::SetBaseValue(const nsAString& aValue, { NS_ASSERTION(aSVGElement, "Null element passed to SetBaseValue"); + mIsBaseSet = PR_TRUE; if (aDoSetAttr) { - mIsBaseSet = PR_TRUE; aSVGElement->SetStringBaseValue(mAttrEnum, aValue); } #ifdef MOZ_SMIL diff --git a/content/svg/document/Makefile.in b/content/svg/document/Makefile.in deleted file mode 100644 index fb49ef1fc85a..000000000000 --- a/content/svg/document/Makefile.in +++ /dev/null @@ -1,48 +0,0 @@ -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is the mozilla svg code. -# -# The Initial Developer of the Original Code is -# Bradley Baetz. -# Portions created by the Initial Developer are Copyright (C) 2001 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -DIRS = src - -include $(topsrcdir)/config/rules.mk - diff --git a/content/xml/Makefile.in b/content/xml/Makefile.in index c6a7d82d3be1..17c158f6ecef 100644 --- a/content/xml/Makefile.in +++ b/content/xml/Makefile.in @@ -42,7 +42,7 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk -PARALLEL_DIRS = content document +PARALLEL_DIRS = content/src document include $(topsrcdir)/config/rules.mk diff --git a/content/xml/content/Makefile.in b/content/xml/content/Makefile.in deleted file mode 100644 index ac483e08e956..000000000000 --- a/content/xml/content/Makefile.in +++ /dev/null @@ -1,48 +0,0 @@ -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -DIRS = src - -include $(topsrcdir)/config/rules.mk - diff --git a/content/xul/templates/Makefile.in b/content/xul/templates/Makefile.in index 0962cfc4621d..f8228cc17fa0 100644 --- a/content/xul/templates/Makefile.in +++ b/content/xul/templates/Makefile.in @@ -46,7 +46,7 @@ MODULE = xultmpl PARALLEL_DIRS = public src ifdef ENABLE_TESTS -TOOL_DIRS += tests +TOOL_DIRS += tests/chrome endif include $(topsrcdir)/config/rules.mk diff --git a/content/xul/templates/tests/Makefile.in b/content/xul/templates/tests/Makefile.in deleted file mode 100644 index be5187c262e0..000000000000 --- a/content/xul/templates/tests/Makefile.in +++ /dev/null @@ -1,49 +0,0 @@ -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Mozilla.org. -# Portions created by the Initial Developer are Copyright (C) 2009 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ -relativesrcdir = content/xul/templates/tests - -include $(DEPTH)/config/autoconf.mk - -DIRS = chrome - -include $(topsrcdir)/config/rules.mk - diff --git a/docshell/Makefile.in b/docshell/Makefile.in index ff74e0d1f7d8..9fc325fd897a 100644 --- a/docshell/Makefile.in +++ b/docshell/Makefile.in @@ -48,7 +48,7 @@ DIRS = \ base \ shistory \ build \ - resources \ + resources/content \ $(NULL) ifdef ENABLE_TESTS diff --git a/docshell/resources/Makefile.in b/docshell/resources/Makefile.in deleted file mode 100644 index 176edb86baa5..000000000000 --- a/docshell/resources/Makefile.in +++ /dev/null @@ -1,48 +0,0 @@ -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is the Mozilla browser. -# -# The Initial Developer of the Original Code is -# Netscape Communications, Inc. -# Portions created by the Initial Developer are Copyright (C) 1999 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Adam Lock -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -DIRS=content - -include $(topsrcdir)/config/rules.mk diff --git a/docshell/test/chrome/bug293235_window.xul b/docshell/test/chrome/bug293235_window.xul index fa003f3f70af..88e73d62d43b 100644 --- a/docshell/test/chrome/bug293235_window.xul +++ b/docshell/test/chrome/bug293235_window.xul @@ -8,13 +8,19 @@ onload="setTimeout(nextTest,0);" title="bug 293235 test"> + diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 7a62b2d07954..7abd58fe23af 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -10178,6 +10178,11 @@ nsGlobalWindow::SetHasOrientationEventListener() EnableDeviceMotionUpdates(); } +void +nsGlobalWindow::RemoveOrientationEventListener() { + DisableDeviceMotionUpdates(); +} + NS_IMETHODIMP nsGlobalWindow::GetURL(nsIDOMMozURLProperty** aURL) { diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index 243e39061b19..8c4f00f26822 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -384,6 +384,7 @@ public: virtual NS_HIDDEN_(nsresult) ForceClose(); virtual NS_HIDDEN_(void) SetHasOrientationEventListener(); + virtual NS_HIDDEN_(void) RemoveOrientationEventListener(); virtual NS_HIDDEN_(void) MaybeUpdateTouchState(); virtual NS_HIDDEN_(void) UpdateTouchState(); virtual NS_HIDDEN_(bool) DispatchCustomEvent(const char *aEventName); diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index d14beeaf21b0..59afb281fe8e 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -1873,8 +1873,8 @@ nsJSContext::CallEventHandler(nsISupports* aTarget, void *aScope, void *aHandler #ifdef NS_FUNCTION_TIMER { JSObject *obj = static_cast(aHandler); - if (obj->isFunctionProxy()) - obj = obj->unwrap(NULL); + if (js::IsFunctionProxy(obj)) + obj = js::UnwrapObject(obj); JSString *id = JS_GetFunctionId(static_cast(JS_GetPrivate(mContext, obj))); JSAutoByteString bytes; const char *name = !id ? "anonymous" : bytes.encode(mContext, id) ? bytes.ptr() : ""; diff --git a/dom/base/nsPIDOMWindow.h b/dom/base/nsPIDOMWindow.h index cdb89309be0e..f3d6606f5a7c 100644 --- a/dom/base/nsPIDOMWindow.h +++ b/dom/base/nsPIDOMWindow.h @@ -576,6 +576,11 @@ public: */ virtual void SetHasOrientationEventListener() = 0; + /** + * Tell this window that we remove an orientation listener + */ + virtual void RemoveOrientationEventListener() = 0; + /** * Set a arguments for this window. This will be set on the window * right away (if there's an existing document) and it will also be diff --git a/dom/indexedDB/test/error_events_abort_transactions_iframe.html b/dom/indexedDB/test/error_events_abort_transactions_iframe.html index a14821b1f2e4..2d385682e80b 100644 --- a/dom/indexedDB/test/error_events_abort_transactions_iframe.html +++ b/dom/indexedDB/test/error_events_abort_transactions_iframe.html @@ -50,7 +50,7 @@ function testSteps() { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); - let uri = SpecialPowers.getDocumentURIObject(window.document); + let uri = window.parent.SpecialPowers.getDocumentURIObject(window.document); Components.classes["@mozilla.org/permissionmanager;1"] .getService(Components.interfaces.nsIPermissionManager) .add(uri, "indexedDB", diff --git a/dom/indexedDB/test/event_propagation_iframe.html b/dom/indexedDB/test/event_propagation_iframe.html index d90a79d4bf13..7fd1942d5603 100644 --- a/dom/indexedDB/test/event_propagation_iframe.html +++ b/dom/indexedDB/test/event_propagation_iframe.html @@ -89,7 +89,7 @@ function testSteps() { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); - let uri = SpecialPowers.getDocumentURIObject(window.document); + let uri = window.parent.SpecialPowers.getDocumentURIObject(window.document); Components.classes["@mozilla.org/permissionmanager;1"] .getService(Components.interfaces.nsIPermissionManager) .add(uri, "indexedDB", diff --git a/dom/tests/mochitest/ajax/scriptaculous/Makefile.in b/dom/tests/mochitest/ajax/scriptaculous/Makefile.in index 22b1fc69ab11..469f94fc534d 100644 --- a/dom/tests/mochitest/ajax/scriptaculous/Makefile.in +++ b/dom/tests/mochitest/ajax/scriptaculous/Makefile.in @@ -46,7 +46,7 @@ include $(DEPTH)/config/autoconf.mk DIRS = \ lib \ src \ - test \ + test/unit \ $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/dom/tests/mochitest/ajax/scriptaculous/test/Makefile.in b/dom/tests/mochitest/ajax/scriptaculous/test/Makefile.in deleted file mode 100644 index a3c2ae76a445..000000000000 --- a/dom/tests/mochitest/ajax/scriptaculous/test/Makefile.in +++ /dev/null @@ -1,50 +0,0 @@ -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Mozilla Foundation. -# Portions created by the Initial Developer are Copyright (C) 2007 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../../../../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ -relativesrcdir = dom/tests/mochitest/ajax/scriptaculous/test - -include $(DEPTH)/config/autoconf.mk - -DIRS = \ - unit \ - $(NULL) - -include $(topsrcdir)/config/rules.mk \ No newline at end of file diff --git a/embedding/android/GeckoApp.java b/embedding/android/GeckoApp.java index 8ca33a227eff..9b77071373a2 100644 --- a/embedding/android/GeckoApp.java +++ b/embedding/android/GeckoApp.java @@ -87,7 +87,7 @@ abstract public class GeckoApp private IntentFilter mConnectivityFilter; private BroadcastReceiver mConnectivityReceiver; - enum LaunchState {PreLaunch, Launching, WaitButton, + enum LaunchState {PreLaunch, Launching, WaitForDebugger, Launched, GeckoRunning, GeckoExiting}; private static LaunchState sLaunchState = LaunchState.PreLaunch; private static boolean sTryCatchAttached = false; @@ -427,21 +427,19 @@ abstract public class GeckoApp } final String action = intent.getAction(); if (ACTION_DEBUG.equals(action) && - checkAndSetLaunchState(LaunchState.Launching, LaunchState.WaitButton)) { - final Button launchButton = new Button(this); - launchButton.setText("Launch"); // don't need to localize - launchButton.setOnClickListener(new Button.OnClickListener() { - public void onClick (View v) { - // hide the button so we can't be launched again - mainLayout.removeView(launchButton); + checkAndSetLaunchState(LaunchState.Launching, LaunchState.WaitForDebugger)) { + + mMainHandler.postDelayed(new Runnable() { + public void run() { + Log.i(LOG_FILE_NAME, "Launching from debug intent after 5s wait"); setLaunchState(LaunchState.Launching); launch(null); } - }); - mainLayout.addView(launchButton, 300, 200); + }, 1000 * 5 /* 5 seconds */); + Log.i(LOG_FILE_NAME, "Intent : ACTION_DEBUG - waiting 5s before launching"); return; } - if (checkLaunchState(LaunchState.WaitButton) || launch(intent)) + if (checkLaunchState(LaunchState.WaitForDebugger) || launch(intent)) return; if (Intent.ACTION_MAIN.equals(action)) { @@ -608,16 +606,6 @@ abstract public class GeckoApp unpackFile(zip, buf, entry, entry.getName()); } } - - // copy any hyphenation dictionaries file into a hyphenation/ directory - Enumeration hyphenEntries = zip.entries(); - while (hyphenEntries.hasMoreElements()) { - ZipEntry entry = hyphenEntries.nextElement(); - if (entry.getName().startsWith("hyphenation/")) { - Log.i("GeckoAppJava", "installing hyphenation : " + entry.getName()); - unpackFile(zip, buf, entry, entry.getName()); - } - } } void removeFiles() throws IOException { diff --git a/embedding/android/GeckoInputConnection.java b/embedding/android/GeckoInputConnection.java index 36fdf0371629..09255782d1d6 100644 --- a/embedding/android/GeckoInputConnection.java +++ b/embedding/android/GeckoInputConnection.java @@ -279,6 +279,11 @@ public class GeckoInputConnection if (req == null) return null; + // Bail out here if gecko isn't running, otherwise we deadlock + // below when waiting for the reply to IME_GET_SELECTION. + if (!GeckoApp.checkLaunchState(GeckoApp.LaunchState.GeckoRunning)) + return null; + //Log.d("GeckoAppJava", "IME: getExtractedText"); ExtractedText extract = new ExtractedText(); diff --git a/embedding/components/Makefile.in b/embedding/components/Makefile.in index 22871b3d3a75..c82ed9465620 100644 --- a/embedding/components/Makefile.in +++ b/embedding/components/Makefile.in @@ -45,11 +45,11 @@ include $(DEPTH)/config/autoconf.mk # You'd think we could skip building ui if XUL is disabled, # but we need to export interface headers from those directories. -DIRS = windowwatcher appstartup find webbrowserpersist commandhandler +DIRS = windowwatcher appstartup/src find webbrowserpersist commandhandler ifdef MOZ_XUL ifdef NS_PRINTING -DIRS += printingui +DIRS += printingui/src endif endif diff --git a/embedding/components/appstartup/Makefile.in b/embedding/components/appstartup/Makefile.in deleted file mode 100644 index 4cfedefd6c0f..000000000000 --- a/embedding/components/appstartup/Makefile.in +++ /dev/null @@ -1,47 +0,0 @@ -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications, Inc. -# Portions created by the Initial Developer are Copyright (C) 2001 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -DIRS = src - -include $(topsrcdir)/config/rules.mk diff --git a/embedding/components/printingui/Makefile.in b/embedding/components/printingui/Makefile.in deleted file mode 100644 index 9f80980e156f..000000000000 --- a/embedding/components/printingui/Makefile.in +++ /dev/null @@ -1,50 +0,0 @@ -#! gmake -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 2000 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Stuart Parmenter -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -DIRS = src - -include $(topsrcdir)/config/rules.mk - diff --git a/embedding/tests/Makefile.in b/embedding/tests/Makefile.in deleted file mode 100644 index 436c61492b14..000000000000 --- a/embedding/tests/Makefile.in +++ /dev/null @@ -1,42 +0,0 @@ -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: Mozilla-sample-code 1.0 -# -# Copyright (c) 2002 Netscape Communications Corporation and -# other contributors -# -# Permission is hereby granted, free of charge, to any person obtaining a -# copy of this Mozilla sample software and associated documentation files -# (the "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to permit -# persons to whom the Software is furnished to do so, subject to the -# following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. -# -# Contributor(s): -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -ifeq ($(OS_ARCH),WINNT) -DIRS = winEmbed -endif - -include $(topsrcdir)/config/rules.mk diff --git a/extensions/spellcheck/Makefile.in b/extensions/spellcheck/Makefile.in index b270c3e90065..934660dcfc33 100644 --- a/extensions/spellcheck/Makefile.in +++ b/extensions/spellcheck/Makefile.in @@ -45,7 +45,7 @@ MODULE = spellchecker DIRS = idl locales hunspell src ifdef ENABLE_TESTS -DIRS += tests +DIRS += tests/chrome endif include $(topsrcdir)/config/rules.mk diff --git a/extensions/spellcheck/hunspell/src/mozHunspell.cpp b/extensions/spellcheck/hunspell/src/mozHunspell.cpp index af09663ac6dc..d66f074b5330 100644 --- a/extensions/spellcheck/hunspell/src/mozHunspell.cpp +++ b/extensions/spellcheck/hunspell/src/mozHunspell.cpp @@ -128,6 +128,7 @@ mozHunspell::Init() nsCOMPtr obs = mozilla::services::GetObserverService(); if (obs) { obs->AddObserver(this, "profile-do-change", PR_TRUE); + obs->AddObserver(this, "profile-after-change", PR_TRUE); } mHunspellReporter = new NS_MEMORY_REPORTER_NAME(Hunspell); @@ -593,7 +594,8 @@ NS_IMETHODIMP mozHunspell::Observe(nsISupports* aSubj, const char *aTopic, const PRUnichar *aData) { - NS_ASSERTION(!strcmp(aTopic, "profile-do-change"), + NS_ASSERTION(!strcmp(aTopic, "profile-do-change") + || !strcmp(aTopic, "profile-after-change"), "Unexpected observer topic"); LoadDictionaryList(); diff --git a/extensions/spellcheck/tests/Makefile.in b/extensions/spellcheck/tests/Makefile.in deleted file mode 100644 index 686fe393fd44..000000000000 --- a/extensions/spellcheck/tests/Makefile.in +++ /dev/null @@ -1,48 +0,0 @@ -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ -relativesrcdir = extensions/spellcheck/tests - -include $(DEPTH)/config/autoconf.mk - -DIRS = chrome - -include $(topsrcdir)/config/rules.mk diff --git a/gfx/layers/DirectedGraph.h b/gfx/layers/DirectedGraph.h new file mode 100644 index 000000000000..ed5d45f1f7b1 --- /dev/null +++ b/gfx/layers/DirectedGraph.h @@ -0,0 +1,148 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Corporation code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Matt Woodrow + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef GFX_DIRECTEDGRAPH_H +#define GFX_DIRECTEDGRAPH_H + +#include "gfxTypes.h" +#include "nsTArray.h" + +namespace mozilla { +namespace layers { + +template +class DirectedGraph { +public: + + class Edge { + public: + Edge(T aFrom, T aTo) : mFrom(aFrom), mTo(aTo) {} + + bool operator==(const Edge& aOther) const + { + return mFrom == aOther.mFrom && mTo == aOther.mTo; + } + + T mFrom; + T mTo; + }; + + class RemoveEdgesToComparator + { + public: + PRBool Equals(const Edge& a, T const& b) const { return a.mTo == b; } + }; + + /** + * Add a new edge to the graph. + */ + void AddEdge(Edge aEdge) + { + NS_ASSERTION(!mEdges.Contains(aEdge), "Adding a duplicate edge!"); + mEdges.AppendElement(aEdge); + } + + void AddEdge(T aFrom, T aTo) + { + AddEdge(Edge(aFrom, aTo)); + } + + /** + * Get the list of edges. + */ + const nsTArray& GetEdgeList() const + { + return mEdges; + } + + /** + * Remove the given edge from the graph. + */ + void RemoveEdge(Edge aEdge) + { + mEdges.RemoveElement(aEdge); + } + + /** + * Remove all edges going into aNode. + */ + void RemoveEdgesTo(T aNode) + { + RemoveEdgesToComparator c; + while (mEdges.RemoveElement(aNode, c)) {} + } + + /** + * Get the number of edges going into aNode. + */ + unsigned int NumEdgesTo(T aNode) + { + unsigned int count = 0; + for (unsigned int i = 0; i < mEdges.Length(); i++) { + if (mEdges.ElementAt(i).mTo == aNode) { + count++; + } + } + return count; + } + + /** + * Get the list of all edges going from aNode + */ + void GetEdgesFrom(T aNode, nsTArray& aResult) + { + for (unsigned int i = 0; i < mEdges.Length(); i++) { + if (mEdges.ElementAt(i).mFrom == aNode) { + aResult.AppendElement(mEdges.ElementAt(i)); + } + } + } + + /** + * Get the total number of edges. + */ + unsigned int GetEdgeCount() { return mEdges.Length(); } + +private: + + nsTArray mEdges; +}; + +} +} + +#endif // GFX_DIRECTEDGRAPH_H diff --git a/gfx/layers/LayerSorter.cpp b/gfx/layers/LayerSorter.cpp new file mode 100644 index 000000000000..9f436ef5d4bc --- /dev/null +++ b/gfx/layers/LayerSorter.cpp @@ -0,0 +1,258 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Corporation code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Matt Woodrow + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "LayerSorter.h" +#include "DirectedGraph.h" +#include "limits.h" + +namespace mozilla { +namespace layers { + +enum LayerSortOrder { + Undefined, + ABeforeB, + BBeforeA, +}; + +/** + * Recover the z component from a 2d transformed point by finding the intersection + * of a line through the point in the z direction and the transformed plane. + * + * We want to solve: + * + * point = normal . (p0 - l0) / normal . l + */ +static gfxFloat RecoverZDepth(const gfx3DMatrix& aTransform, const gfxPoint& aPoint) +{ + const gfxPoint3D l(0, 0, 1); + gfxPoint3D l0 = gfxPoint3D(aPoint.x, aPoint.y, 0); + gfxPoint3D p0 = aTransform.Transform3D(gfxPoint3D(0, 0, 0)); + gfxPoint3D normal = aTransform.GetNormalVector(); + + gfxFloat n = normal.DotProduct(p0 - l0); + gfxFloat d = normal.DotProduct(l); + + if (!d) { + return 0; + } + + return n/d; +} + +/** + * Determine if this transform layer should be drawn before another when they + * are both preserve-3d children. + * + * We want to find the relative z depths of the 2 layers at points where they + * intersect when projected onto the 2d screen plane. + * + * If the ordering is consistent at all intersection points, then we have + * a definitive order, otherwise the 2 layers must actually intersect in 3d + * space, and we just order these arbitrarily. + */ +static LayerSortOrder CompareDepth(Layer* aOne, Layer* aTwo) { + gfxRect ourRect = aOne->GetEffectiveVisibleRegion().GetBounds(); + gfxRect otherRect = aTwo->GetEffectiveVisibleRegion().GetBounds(); + + gfx3DMatrix ourTransform = aOne->GetTransform(); + gfx3DMatrix otherTransform = aTwo->GetTransform(); + + // Transform both rectangles and project into 2d space. + gfxQuad ourTransformedRect = ourTransform.TransformRect(ourRect); + gfxQuad otherTransformedRect = otherTransform.TransformRect(otherRect); + + // Make a list of all points that are within the other rect. + nsTArray points; + for (PRUint32 i=0; i<4; i++) { + if (ourTransformedRect.Contains(otherTransformedRect.mPoints[i])) { + points.AppendElement(otherTransformedRect.mPoints[i]); + } + if (otherTransformedRect.Contains(ourTransformedRect.mPoints[i])) { + points.AppendElement(ourTransformedRect.mPoints[i]); + } + } + + // No intersections, no defined order between these layers. + if (points.IsEmpty()) { + return Undefined; + } + + // Find the relative Z depths of each intersection point and check that the layers are in the same order. + bool drawBefore = false; + for (PRUint32 i = 0; i < points.Length(); i++) { + bool temp = RecoverZDepth(ourTransform, points.ElementAt(i)) <= RecoverZDepth(otherTransform, points.ElementAt(i)); + if (i == 0) { + drawBefore = temp; + } else if (drawBefore != temp) { + // Mixed ordering means an intersection in 3d space that we can't resolve without plane splitting + // or depth buffering. Store this as having no defined order for now. + return Undefined; + } + } + if (drawBefore) { + return ABeforeB; + } + return BBeforeA; +} + +#ifdef DEBUG +static bool gDumpLayerSortList = getenv("MOZ_DUMP_LAYER_SORT_LIST") != 0; + +static void DumpLayerList(nsTArray& aLayers) +{ + for (PRUint32 i = 0; i < aLayers.Length(); i++) { + fprintf(stderr, "%p, ", aLayers.ElementAt(i)); + } + fprintf(stderr, "\n"); +} + +static void DumpEdgeList(DirectedGraph& aGraph) +{ + nsTArray::Edge> edges = aGraph.GetEdgeList(); + + for (PRUint32 i = 0; i < edges.Length(); i++) { + fprintf(stderr, "From: %p, To: %p\n", edges.ElementAt(i).mFrom, edges.ElementAt(i).mTo); + } +} +#endif + +// The maximum number of layers that we will attempt to sort. Anything +// greater than this will be left unsorted. We should consider enabling +// depth buffering for the scene in this case. +#define MAX_SORTABLE_LAYERS 100 + +void SortLayersBy3DZOrder(nsTArray& aLayers) +{ + PRUint32 nodeCount = aLayers.Length(); + if (nodeCount > MAX_SORTABLE_LAYERS) { + return; + } + DirectedGraph graph; + +#ifdef DEBUG + if (gDumpLayerSortList) { + fprintf(stderr, " --- Layers before sorting: --- \n"); + DumpLayerList(aLayers); + } +#endif + + // Iterate layers and determine edges. + for (PRUint32 i = 0; i < nodeCount; i++) { + for (PRUint32 j = i + 1; j < nodeCount; j++) { + Layer* a = aLayers.ElementAt(i); + Layer* b = aLayers.ElementAt(j); + LayerSortOrder order = CompareDepth(a, b); + if (order == ABeforeB) { + graph.AddEdge(a, b); + } else if (order == BBeforeA) { + graph.AddEdge(b, a); + } + } + } + +#ifdef DEBUG + if (gDumpLayerSortList) { + fprintf(stderr, " --- Edge List: --- \n"); + DumpEdgeList(graph); + } +#endif + + // Build a new array using the graph. + nsTArray noIncoming; + nsTArray sortedList; + + // Make a list of all layers with no incoming edges. + noIncoming.AppendElements(aLayers); + const nsTArray::Edge>& edges = graph.GetEdgeList(); + for (PRUint32 i = 0; i < edges.Length(); i++) { + noIncoming.RemoveElement(edges.ElementAt(i).mTo); + } + + // Move each item without incoming edges into the sorted list, + // and remove edges from it. + while (!noIncoming.IsEmpty()) { + PRUint32 last = noIncoming.Length() - 1; + + Layer* layer = noIncoming.ElementAt(last); + + noIncoming.RemoveElementAt(last); + sortedList.AppendElement(layer); + + nsTArray::Edge> outgoing; + graph.GetEdgesFrom(layer, outgoing); + for (PRUint32 i = 0; i < outgoing.Length(); i++) { + DirectedGraph::Edge edge = outgoing.ElementAt(i); + graph.RemoveEdge(edge); + if (!graph.NumEdgesTo(edge.mTo)) { + // If this node also has no edges now, add it to the list + noIncoming.AppendElement(edge.mTo); + } + } + + // If there are no nodes without incoming edges, but there + // are still edges, then we have a cycle. + if (noIncoming.IsEmpty() && graph.GetEdgeCount()) { + // Find the node with the least incoming edges. + PRUint32 minEdges = UINT_MAX; + Layer* minNode = nsnull; + for (PRUint32 i = 0; i < aLayers.Length(); i++) { + PRUint32 edgeCount = graph.NumEdgesTo(aLayers.ElementAt(i)); + if (edgeCount && edgeCount < minEdges) { + minEdges = edgeCount; + minNode = aLayers.ElementAt(i); + } + } + + // Remove all of them! + graph.RemoveEdgesTo(minNode); + noIncoming.AppendElement(minNode); + } + } + NS_ASSERTION(!graph.GetEdgeCount(), "Cycles detected!"); +#ifdef DEBUG + if (gDumpLayerSortList) { + fprintf(stderr, " --- Layers after sorting: --- \n"); + DumpLayerList(sortedList); + } +#endif + + aLayers.Clear(); + aLayers.AppendElements(sortedList); +} + +} +} diff --git a/gfx/layers/LayerSorter.h b/gfx/layers/LayerSorter.h new file mode 100644 index 000000000000..e36bff011e7c --- /dev/null +++ b/gfx/layers/LayerSorter.h @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Corporation code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Matt Woodrow + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef GFX_LAYERSORTER_H +#define GFX_LAYERSORTER_H + +#include "Layers.h" + +namespace mozilla { +namespace layers { + +void SortLayersBy3DZOrder(nsTArray& aLayers); + +} +} +#endif /* GFX_LAYERSORTER_H */ diff --git a/gfx/layers/Layers.cpp b/gfx/layers/Layers.cpp index d256ad806049..cc994743dc96 100644 --- a/gfx/layers/Layers.cpp +++ b/gfx/layers/Layers.cpp @@ -48,6 +48,7 @@ #include "gfxUtils.h" #include "nsPrintfCString.h" #include "mozilla/Util.h" +#include "LayerSorter.h" using namespace mozilla::layers; using namespace mozilla::gfx; @@ -415,11 +416,35 @@ ContainerLayer::HasMultipleChildren() return PR_FALSE; } +void +ContainerLayer::SortChildrenBy3DZOrder(nsTArray& aArray) +{ + nsAutoTArray toSort; + + for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) { + ContainerLayer* container = l->AsContainerLayer(); + if (container && container->GetContentFlags() & CONTENT_PRESERVE_3D) { + toSort.AppendElement(l); + } else { + if (toSort.Length() > 0) { + SortLayersBy3DZOrder(toSort); + aArray.MoveElementsFrom(toSort); + } + aArray.AppendElement(l); + } + } + if (toSort.Length() > 0) { + SortLayersBy3DZOrder(toSort); + aArray.MoveElementsFrom(toSort); + } +} + void ContainerLayer::DefaultComputeEffectiveTransforms(const gfx3DMatrix& aTransformToSurface) { gfxMatrix residual; gfx3DMatrix idealTransform = GetLocalTransform()*aTransformToSurface; + idealTransform.ProjectTo2D(); mEffectiveTransform = SnapTransform(idealTransform, gfxRect(0, 0, 0, 0), &residual); bool useIntermediateSurface; @@ -429,9 +454,7 @@ ContainerLayer::DefaultComputeEffectiveTransforms(const gfx3DMatrix& aTransformT } else { useIntermediateSurface = PR_FALSE; gfxMatrix contTransform; - if (!mEffectiveTransform.Is2D(&contTransform)) { - useIntermediateSurface = PR_TRUE; - } else if ( + if (!mEffectiveTransform.Is2D(&contTransform) || #ifdef MOZ_GFX_OPTIMIZE_MOBILE !contTransform.PreservesAxisAlignedRectangles()) { #else diff --git a/gfx/layers/Layers.h b/gfx/layers/Layers.h index c6d1f61bb67d..bcd5f1985190 100644 --- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -48,6 +48,7 @@ #include "gfx3DMatrix.h" #include "gfxColor.h" #include "gfxPattern.h" +#include "nsTArray.h" #include "mozilla/gfx/2D.h" @@ -577,7 +578,13 @@ public: * paint time. * This should never be set at the same time as CONTENT_OPAQUE. */ - CONTENT_COMPONENT_ALPHA = 0x02 + CONTENT_COMPONENT_ALPHA = 0x02, + + /** + * If this is set then this layer is part of a preserve-3d group, and should + * be sorted with sibling layers that are also part of the same group. + */ + CONTENT_PRESERVE_3D = 0x04 }; /** * CONSTRUCTION PHASE ONLY @@ -1095,6 +1102,8 @@ public: virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs); + void SortChildrenBy3DZOrder(nsTArray& aArray); + // These getters can be used anytime. virtual ContainerLayer* AsContainerLayer() { return this; } diff --git a/gfx/layers/Makefile.in b/gfx/layers/Makefile.in index fd8ce59f885c..61646daf74d1 100644 --- a/gfx/layers/Makefile.in +++ b/gfx/layers/Makefile.in @@ -66,6 +66,7 @@ EXPORTS = \ LayerManagerOGL.h \ LayerManagerOGLProgram.h \ ReadbackLayer.h \ + LayerSorter.h \ $(NULL) CPPSRCS = \ @@ -80,6 +81,7 @@ CPPSRCS = \ ImageLayerOGL.cpp \ LayerManagerOGL.cpp \ ThebesLayerOGL.cpp \ + LayerSorter.cpp \ $(NULL) ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa) diff --git a/gfx/layers/basic/BasicLayers.cpp b/gfx/layers/basic/BasicLayers.cpp index e77c15fe80ce..e0318c2ca270 100644 --- a/gfx/layers/basic/BasicLayers.cpp +++ b/gfx/layers/basic/BasicLayers.cpp @@ -532,6 +532,9 @@ public: ThebesLayer::ComputeEffectiveTransforms(aTransformToSurface); } + // Sync front/back buffers content + virtual void SyncFrontBufferToBackBuffer() {} + protected: BasicLayerManager* BasicManager() { @@ -656,6 +659,7 @@ BasicThebesLayer::PaintThebes(gfxContext* aContext, if (aReadback && UsedForReadback()) { aReadback->GetThebesLayerUpdates(this, &readbackUpdates); } + SyncFrontBufferToBackBuffer(); bool canUseOpaqueSurface = CanUseOpaqueSurface(); Buffer::ContentType contentType = @@ -1887,13 +1891,16 @@ BasicLayerManager::PaintLayer(gfxContext* aTarget, } } else { ReadbackProcessor readback; + ContainerLayer* container = static_cast(aLayer); if (IsRetained()) { - ContainerLayer* container = static_cast(aLayer); readback.BuildUpdates(container); } + + nsAutoTArray children; + container->SortChildrenBy3DZOrder(children); - for (; child; child = child->GetNextSibling()) { - PaintLayer(groupTarget, child, aCallback, aCallbackData, &readback); + for (PRUint32 i = 0; i < children.Length(); i++) { + PaintLayer(groupTarget, children.ElementAt(i), aCallback, aCallbackData, &readback); if (mTransactionIncomplete) break; } @@ -2154,13 +2161,13 @@ public: BasicShadowableThebesLayer(BasicShadowLayerManager* aManager) : BasicThebesLayer(aManager) , mIsNewBuffer(false) + , mFrontAndBackBufferDiffer(false) { MOZ_COUNT_CTOR(BasicShadowableThebesLayer); } virtual ~BasicShadowableThebesLayer() { - if (IsSurfaceDescriptorValid(mBackBuffer)) - BasicManager()->ShadowLayerForwarder::DestroySharedSurface(&mBackBuffer); + DestroyBackBuffer(); MOZ_COUNT_DTOR(BasicShadowableThebesLayer); } @@ -2173,7 +2180,7 @@ public: virtual ShadowableLayer* AsShadowableLayer() { return this; } virtual bool MustRetainContent() { return HasShadow(); } - void SetBackBufferAndAttrs(const ThebesBuffer& aBuffer, + void SetBackBufferAndAttrs(const OptionalThebesBuffer& aBuffer, const nsIntRegion& aValidRegion, const OptionalThebesBuffer& aReadOnlyFrontBuffer, const nsIntRegion& aFrontUpdatedRegion); @@ -2186,6 +2193,8 @@ public: virtual BasicShadowableThebesLayer* AsThebes() { return this; } + virtual void SyncFrontBufferToBackBuffer(); + private: BasicShadowLayerManager* BasicManager() { @@ -2204,46 +2213,89 @@ private: NS_OVERRIDE virtual already_AddRefed CreateBuffer(Buffer::ContentType aType, const nsIntSize& aSize); + void DestroyBackBuffer() + { + if (IsSurfaceDescriptorValid(mBackBuffer)) { + BasicManager()->ShadowLayerForwarder::DestroySharedSurface(&mBackBuffer); + } + } + // This describes the gfxASurface we hand to mBuffer. We keep a // copy of the descriptor here so that we can call // DestroySharedSurface() on the descriptor. SurfaceDescriptor mBackBuffer; + nsIntRect mBackBufferRect; + nsIntPoint mBackBufferRectRotation; bool mIsNewBuffer; + OptionalThebesBuffer mROFrontBuffer; + nsIntRegion mFrontUpdatedRegion; + nsIntRegion mFrontValidRegion; + PRPackedBool mFrontAndBackBufferDiffer; }; void -BasicShadowableThebesLayer::SetBackBufferAndAttrs(const ThebesBuffer& aBuffer, +BasicShadowableThebesLayer::SetBackBufferAndAttrs(const OptionalThebesBuffer& aBuffer, const nsIntRegion& aValidRegion, const OptionalThebesBuffer& aReadOnlyFrontBuffer, const nsIntRegion& aFrontUpdatedRegion) { - mBackBuffer = aBuffer.buffer(); - nsRefPtr backBuffer = BasicManager()->OpenDescriptor(mBackBuffer); + if (OptionalThebesBuffer::Tnull_t == aBuffer.type()) { + mBackBuffer = SurfaceDescriptor(); + } else { + mBackBuffer = aBuffer.get_ThebesBuffer().buffer(); + mBackBufferRect = aBuffer.get_ThebesBuffer().rect(); + mBackBufferRectRotation = aBuffer.get_ThebesBuffer().rotation(); + } + mFrontAndBackBufferDiffer = true; + mROFrontBuffer = aReadOnlyFrontBuffer; + mFrontUpdatedRegion = aFrontUpdatedRegion; + mFrontValidRegion = aValidRegion; +} - if (OptionalThebesBuffer::Tnull_t == aReadOnlyFrontBuffer.type()) { +void +BasicShadowableThebesLayer::SyncFrontBufferToBackBuffer() +{ + if (!mFrontAndBackBufferDiffer) { + return; + } + + nsRefPtr backBuffer; + if (!IsSurfaceDescriptorValid(mBackBuffer)) { + NS_ABORT_IF_FALSE(mROFrontBuffer.type() == OptionalThebesBuffer::TThebesBuffer, + "should have a front RO buffer by now"); + const ThebesBuffer roFront = mROFrontBuffer.get_ThebesBuffer(); + nsRefPtr roFrontBuffer = BasicManager()->OpenDescriptor(roFront.buffer()); + backBuffer = CreateBuffer(roFrontBuffer->GetContentType(), roFrontBuffer->GetSize()); + } else { + backBuffer = BasicManager()->OpenDescriptor(mBackBuffer); + } + mFrontAndBackBufferDiffer = false; + + if (OptionalThebesBuffer::Tnull_t == mROFrontBuffer.type()) { // We didn't get back a read-only ref to our old back buffer (the // parent's new front buffer). If the parent is pushing updates // to a texture it owns, then we probably got back the same buffer // we pushed in the update and all is well. If not, ... - mValidRegion = aValidRegion; - mBuffer.SetBackingBuffer(backBuffer, aBuffer.rect(), aBuffer.rotation()); + mValidRegion = mFrontValidRegion; + mBuffer.SetBackingBuffer(backBuffer, mBackBufferRect, mBackBufferRectRotation); return; } MOZ_LAYERS_LOG(("BasicShadowableThebes(%p): reading back ", this, - aFrontUpdatedRegion.GetBounds().x, - aFrontUpdatedRegion.GetBounds().y, - aFrontUpdatedRegion.GetBounds().width, - aFrontUpdatedRegion.GetBounds().height)); + mFrontUpdatedRegion.GetBounds().x, + mFrontUpdatedRegion.GetBounds().y, + mFrontUpdatedRegion.GetBounds().width, + mFrontUpdatedRegion.GetBounds().height)); - const ThebesBuffer roFront = aReadOnlyFrontBuffer.get_ThebesBuffer(); + const ThebesBuffer roFront = mROFrontBuffer.get_ThebesBuffer(); nsRefPtr roFrontBuffer = BasicManager()->OpenDescriptor(roFront.buffer()); mBuffer.SetBackingBufferAndUpdateFrom( backBuffer, roFrontBuffer, roFront.rect(), roFront.rotation(), - aFrontUpdatedRegion); + mFrontUpdatedRegion); + mIsNewBuffer = false; // Now the new back buffer has the same (interesting) pixels as the // new front buffer, and mValidRegion et al. are correct wrt the new // back buffer (i.e. as they were for the old back buffer) @@ -2305,36 +2357,21 @@ BasicShadowableThebesLayer::CreateBuffer(Buffer::ContentType aType, aSize.width, aSize.height)); if (IsSurfaceDescriptorValid(mBackBuffer)) { - BasicManager()->DestroyedThebesBuffer(BasicManager()->Hold(this), - mBackBuffer); mBackBuffer = SurfaceDescriptor(); } // XXX error handling - SurfaceDescriptor tmpFront; - if (BasicManager()->ShouldDoubleBuffer()) { - if (!BasicManager()->AllocDoubleBuffer(gfxIntSize(aSize.width, aSize.height), - aType, - &tmpFront, - &mBackBuffer)) { + if (!BasicManager()->AllocBuffer(gfxIntSize(aSize.width, aSize.height), + aType, + &mBackBuffer)) { NS_RUNTIMEABORT("creating ThebesLayer 'back buffer' failed!"); - } - } else { - if (!BasicManager()->AllocBuffer(gfxIntSize(aSize.width, aSize.height), - aType, - &mBackBuffer)) { - NS_RUNTIMEABORT("creating ThebesLayer 'back buffer' failed!"); - } } NS_ABORT_IF_FALSE(!mIsNewBuffer, "Bad! Did we create a buffer twice without painting?"); + mIsNewBuffer = true; - BasicManager()->CreatedThebesBuffer(BasicManager()->Hold(this), - nsIntRegion(), - nsIntRect(), - tmpFront); return BasicManager()->OpenDescriptor(mBackBuffer); } @@ -2681,9 +2718,6 @@ public: MOZ_COUNT_DTOR(BasicShadowThebesLayer); } - virtual void SetFrontBuffer(const OptionalThebesBuffer& aNewFront, - const nsIntRegion& aValidRegion); - virtual void SetValidRegion(const nsIntRegion& aRegion) { mOldValidRegion = mValidRegion; @@ -2698,7 +2732,7 @@ public: virtual void Swap(const ThebesBuffer& aNewFront, const nsIntRegion& aUpdatedRegion, - ThebesBuffer* aNewBack, nsIntRegion* aNewBackValidRegion, + OptionalThebesBuffer* aNewBack, nsIntRegion* aNewBackValidRegion, OptionalThebesBuffer* aReadOnlyFront, nsIntRegion* aFrontUpdatedRegion); virtual void DestroyFrontBuffer() @@ -2709,6 +2743,7 @@ public: if (IsSurfaceDescriptorValid(mFrontBufferDescriptor)) { mAllocator->DestroySharedSurface(&mFrontBufferDescriptor); + mFrontBufferDescriptor = SurfaceDescriptor(); } } @@ -2733,48 +2768,46 @@ private: nsIntRegion mOldValidRegion; }; -void -BasicShadowThebesLayer::SetFrontBuffer(const OptionalThebesBuffer& aNewFront, - const nsIntRegion& aValidRegion) -{ - mValidRegion = mOldValidRegion = aValidRegion; - - NS_ABORT_IF_FALSE(OptionalThebesBuffer::Tnull_t != aNewFront.type(), - "aNewFront must be valid here!"); - - const ThebesBuffer newFront = aNewFront.get_ThebesBuffer(); - nsRefPtr newFrontBuffer = - BasicManager()->OpenDescriptor(newFront.buffer()); - - nsRefPtr unused; - nsIntRect unusedRect; - nsIntPoint unusedRotation; - mFrontBuffer.Swap(newFrontBuffer, newFront.rect(), newFront.rotation(), - getter_AddRefs(unused), &unusedRect, &unusedRotation); - mFrontBufferDescriptor = newFront.buffer(); -} - void BasicShadowThebesLayer::Swap(const ThebesBuffer& aNewFront, const nsIntRegion& aUpdatedRegion, - ThebesBuffer* aNewBack, + OptionalThebesBuffer* aNewBack, nsIntRegion* aNewBackValidRegion, OptionalThebesBuffer* aReadOnlyFront, nsIntRegion* aFrontUpdatedRegion) { + nsRefPtr newFrontBuffer = + BasicManager()->OpenDescriptor(aNewFront.buffer()); + + if (IsSurfaceDescriptorValid(mFrontBufferDescriptor)) { + nsRefPtr currentFront = BasicManager()->OpenDescriptor(mFrontBufferDescriptor); + if (currentFront->GetSize() != newFrontBuffer->GetSize()) { + // Current front buffer is obsolete + DestroyFrontBuffer(); + } + } // This code relies on Swap() arriving *after* attribute mutations. - aNewBack->buffer() = mFrontBufferDescriptor; + if (IsSurfaceDescriptorValid(mFrontBufferDescriptor)) { + *aNewBack = ThebesBuffer(); + aNewBack->get_ThebesBuffer().buffer() = mFrontBufferDescriptor; + } else { + *aNewBack = null_t(); + } // We have to invalidate the pixels painted into the new buffer. // They might overlap with our old pixels. aNewBackValidRegion->Sub(mOldValidRegion, aUpdatedRegion); - nsRefPtr newFrontBuffer = - BasicManager()->OpenDescriptor(aNewFront.buffer()); - nsRefPtr unused; + nsIntRect backRect; + nsIntPoint backRotation; mFrontBuffer.Swap( newFrontBuffer, aNewFront.rect(), aNewFront.rotation(), - getter_AddRefs(unused), &aNewBack->rect(), &aNewBack->rotation()); + getter_AddRefs(unused), &backRect, &backRotation); + + if (aNewBack->type() != OptionalThebesBuffer::Tnull_t) { + aNewBack->get_ThebesBuffer().rect() = backRect; + aNewBack->get_ThebesBuffer().rotation() = backRotation; + } mFrontBufferDescriptor = aNewFront.buffer(); diff --git a/gfx/layers/d3d10/ContainerLayerD3D10.cpp b/gfx/layers/d3d10/ContainerLayerD3D10.cpp index c7082423d496..9a39d1709fb1 100644 --- a/gfx/layers/d3d10/ContainerLayerD3D10.cpp +++ b/gfx/layers/d3d10/ContainerLayerD3D10.cpp @@ -265,12 +265,14 @@ ContainerLayerD3D10::RenderLayer() oldD3D10Scissor.right - oldD3D10Scissor.left, oldD3D10Scissor.bottom - oldD3D10Scissor.top); + nsAutoTArray children; + SortChildrenBy3DZOrder(children); + /* * Render this container's contents. */ - for (LayerD3D10* layerToRender = GetFirstChildD3D10(); - layerToRender != nsnull; - layerToRender = GetNextSiblingD3D10(layerToRender)) { + for (PRUint32 i = 0; i < children.Length(); i++) { + LayerD3D10* layerToRender = static_cast(children.ElementAt(i)->ImplData()); if (layerToRender->GetLayer()->GetEffectiveVisibleRegion().IsEmpty()) { continue; diff --git a/gfx/layers/d3d10/LayerManagerD3D10.cpp b/gfx/layers/d3d10/LayerManagerD3D10.cpp index 6248221483a8..f3934d602503 100644 --- a/gfx/layers/d3d10/LayerManagerD3D10.cpp +++ b/gfx/layers/d3d10/LayerManagerD3D10.cpp @@ -729,11 +729,6 @@ LayerManagerD3D10::Render() windowLayer = new WindowLayer(this); windowLayer->SetShadow(ConstructShadowFor(windowLayer)); CreatedThebesLayer(windowLayer); - ShadowLayerForwarder::CreatedThebesBuffer(windowLayer, - contentRect, - contentRect, - SurfaceDescriptor()); - mRootForShadowTree->InsertAfter(windowLayer, nsnull); ShadowLayerForwarder::InsertAfter(mRootForShadowTree, windowLayer); } diff --git a/gfx/layers/d3d10/ThebesLayerD3D10.cpp b/gfx/layers/d3d10/ThebesLayerD3D10.cpp index 4acd6f1f9e35..a640ce188502 100644 --- a/gfx/layers/d3d10/ThebesLayerD3D10.cpp +++ b/gfx/layers/d3d10/ThebesLayerD3D10.cpp @@ -469,18 +469,10 @@ ShadowThebesLayerD3D10::~ShadowThebesLayerD3D10() { } -void -ShadowThebesLayerD3D10::SetFrontBuffer(const OptionalThebesBuffer& aNewFront, - const nsIntRegion& aValidRegion) -{ - NS_ABORT_IF_FALSE(OptionalThebesBuffer::Tnull_t == aNewFront.type(), - "Expected dummy front buffer initially"); -} - void ShadowThebesLayerD3D10::Swap( const ThebesBuffer& aNewFront, const nsIntRegion& aUpdatedRegion, - ThebesBuffer* aNewBack, nsIntRegion* aNewBackValidRegion, + OptionalThebesBuffer* aNewBack, nsIntRegion* aNewBackValidRegion, OptionalThebesBuffer* aReadOnlyFront, nsIntRegion* aFrontUpdatedRegion) { nsRefPtr newBackBuffer = mTexture; @@ -490,7 +482,7 @@ ShadowThebesLayerD3D10::Swap( // The content process tracks back/front buffers on its own, so // the newBack is in essence unused. - aNewBack->buffer() = aNewFront.buffer(); + aNewBack->get_ThebesBuffer().buffer() = aNewFront.buffer(); // The content process doesn't need to read back from the front // buffer (yet). diff --git a/gfx/layers/d3d10/ThebesLayerD3D10.h b/gfx/layers/d3d10/ThebesLayerD3D10.h index 95a987e4feb7..5b3f03bfb332 100644 --- a/gfx/layers/d3d10/ThebesLayerD3D10.h +++ b/gfx/layers/d3d10/ThebesLayerD3D10.h @@ -108,12 +108,9 @@ public: ShadowThebesLayerD3D10(LayerManagerD3D10* aManager); virtual ~ShadowThebesLayerD3D10(); - // ShadowThebesLayer impl - virtual void SetFrontBuffer(const OptionalThebesBuffer& aNewFront, - const nsIntRegion& aValidRegion); virtual void Swap(const ThebesBuffer& aNewFront, const nsIntRegion& aUpdatedRegion, - ThebesBuffer* aNewBack, nsIntRegion* aNewBackValidRegion, + OptionalThebesBuffer* aNewBack, nsIntRegion* aNewBackValidRegion, OptionalThebesBuffer* aReadOnlyFront, nsIntRegion* aFrontUpdatedRegion); virtual void DestroyFrontBuffer(); diff --git a/gfx/layers/d3d9/ContainerLayerD3D9.cpp b/gfx/layers/d3d9/ContainerLayerD3D9.cpp index fc04857ca876..ebe2c8d8cc96 100644 --- a/gfx/layers/d3d9/ContainerLayerD3D9.cpp +++ b/gfx/layers/d3d9/ContainerLayerD3D9.cpp @@ -247,12 +247,14 @@ ContainerRender(Container* aContainer, aContainer->mParent->SupportsComponentAlphaChildren()); } + nsAutoTArray children; + aContainer->SortChildrenBy3DZOrder(children); + /* * Render this container's contents. */ - for (LayerD3D9* layerToRender = aContainer->GetFirstChildD3D9(); - layerToRender != nsnull; - layerToRender = GetNextSiblingD3D9(layerToRender)) { + for (PRUint32 i = 0; i < children.Length(); i++) { + LayerD3D9* layerToRender = static_cast(children.ElementAt(i)->ImplData()); if (layerToRender->GetLayer()->GetEffectiveVisibleRegion().IsEmpty()) { continue; diff --git a/gfx/layers/d3d9/ThebesLayerD3D9.cpp b/gfx/layers/d3d9/ThebesLayerD3D9.cpp index b665887840a3..4e83dccabf66 100644 --- a/gfx/layers/d3d9/ThebesLayerD3D9.cpp +++ b/gfx/layers/d3d9/ThebesLayerD3D9.cpp @@ -619,25 +619,17 @@ ShadowThebesLayerD3D9::~ShadowThebesLayerD3D9() {} void -ShadowThebesLayerD3D9::SetFrontBuffer(const OptionalThebesBuffer& aNewFront, - const nsIntRegion& aValidRegion) +ShadowThebesLayerD3D9::Swap(const ThebesBuffer& aNewFront, + const nsIntRegion& aUpdatedRegion, + OptionalThebesBuffer* aNewBack, + nsIntRegion* aNewBackValidRegion, + OptionalThebesBuffer* aReadOnlyFront, + nsIntRegion* aFrontUpdatedRegion) { if (!mBuffer) { mBuffer = new ShadowBufferD3D9(this); } - NS_ASSERTION(OptionalThebesBuffer::Tnull_t == aNewFront.type(), - "Only one system-memory buffer expected"); -} - -void -ShadowThebesLayerD3D9::Swap(const ThebesBuffer& aNewFront, - const nsIntRegion& aUpdatedRegion, - ThebesBuffer* aNewBack, - nsIntRegion* aNewBackValidRegion, - OptionalThebesBuffer* aReadOnlyFront, - nsIntRegion* aFrontUpdatedRegion) -{ if (mBuffer) { nsRefPtr surf = ShadowLayerForwarder::OpenDescriptor(aNewFront.buffer()); mBuffer->Upload(surf, GetVisibleRegion().GetBounds()); diff --git a/gfx/layers/d3d9/ThebesLayerD3D9.h b/gfx/layers/d3d9/ThebesLayerD3D9.h index b0b7fa1f877b..bade4e022983 100644 --- a/gfx/layers/d3d9/ThebesLayerD3D9.h +++ b/gfx/layers/d3d9/ThebesLayerD3D9.h @@ -120,12 +120,9 @@ public: ShadowThebesLayerD3D9(LayerManagerD3D9 *aManager); virtual ~ShadowThebesLayerD3D9(); - // ShadowThebesLayer impl - virtual void SetFrontBuffer(const OptionalThebesBuffer& aNewFront, - const nsIntRegion& aValidRegion); virtual void Swap(const ThebesBuffer& aNewFront, const nsIntRegion& aUpdatedRegion, - ThebesBuffer* aNewBack, nsIntRegion* aNewBackValidRegion, + OptionalThebesBuffer* aNewBack, nsIntRegion* aNewBackValidRegion, OptionalThebesBuffer* aReadOnlyFront, nsIntRegion* aFrontUpdatedRegion); virtual void DestroyFrontBuffer(); diff --git a/gfx/layers/ipc/PLayers.ipdl b/gfx/layers/ipc/PLayers.ipdl index 3e59964f5b4c..af5da3bf3da3 100644 --- a/gfx/layers/ipc/PLayers.ipdl +++ b/gfx/layers/ipc/PLayers.ipdl @@ -109,17 +109,6 @@ union CanvasSurface { null_t; }; -// For the "buffer creation" operations, we send an initial front -// buffer that only contains (transparent) black pixels just so that -// we can swap it back after the first OpPaint without a special case. - -struct OpCreateThebesBuffer { - PLayer layer; - OptionalThebesBuffer initialFront; - nsIntRegion frontValidRegion; -}; -struct OpDestroyThebesFrontBuffer { PLayer layer; }; - // Change a layer's attributes struct CommonLayerAttributes { nsIntRegion visibleRegion; @@ -194,8 +183,6 @@ union Edit { OpCreateImageLayer; OpCreateColorLayer; OpCreateCanvasLayer; - OpCreateThebesBuffer; - OpDestroyThebesFrontBuffer; OpSetLayerAttributes; @@ -217,7 +204,7 @@ struct OpImageSwap { PLayer layer; SharedImage newBackImage; }; struct OpThebesBufferSwap { PLayer layer; - ThebesBuffer newBackBuffer; + OptionalThebesBuffer newBackBuffer; nsIntRegion newValidRegion; // If the parent took the child's old back buffer and returned its // old front buffer, |readOnlyFrontBuffer| may (if non-null) contain diff --git a/gfx/layers/ipc/ShadowLayers.cpp b/gfx/layers/ipc/ShadowLayers.cpp index df262475d4bf..b61f94178ebf 100644 --- a/gfx/layers/ipc/ShadowLayers.cpp +++ b/gfx/layers/ipc/ShadowLayers.cpp @@ -184,31 +184,6 @@ ShadowLayerForwarder::CreatedCanvasLayer(ShadowableLayer* aCanvas) CreatedLayer(mTxn, aCanvas); } -void -ShadowLayerForwarder::CreatedThebesBuffer(ShadowableLayer* aThebes, - const nsIntRegion& aFrontValidRegion, - const nsIntRect& aBufferRect, - const SurfaceDescriptor& aTempFrontBuffer) -{ - OptionalThebesBuffer buffer = null_t(); - if (IsSurfaceDescriptorValid(aTempFrontBuffer)) { - buffer = ThebesBuffer(aTempFrontBuffer, - aBufferRect, - nsIntPoint(0, 0)); - } - mTxn->AddEdit(OpCreateThebesBuffer(NULL, Shadow(aThebes), - buffer, - aFrontValidRegion)); -} - -void -ShadowLayerForwarder::DestroyedThebesBuffer(ShadowableLayer* aThebes, - const SurfaceDescriptor& aBackBufferToDestroy) -{ - mTxn->AddEdit(OpDestroyThebesFrontBuffer(NULL, Shadow(aThebes))); - mTxn->AddBufferToDestroy(aBackBufferToDestroy); -} - void ShadowLayerForwarder::Mutated(ShadowableLayer* aMutant) { diff --git a/gfx/layers/ipc/ShadowLayers.h b/gfx/layers/ipc/ShadowLayers.h index bf1a713c5cf0..849e80ade0ec 100644 --- a/gfx/layers/ipc/ShadowLayers.h +++ b/gfx/layers/ipc/ShadowLayers.h @@ -140,38 +140,6 @@ public: void CreatedColorLayer(ShadowableLayer* aColor); void CreatedCanvasLayer(ShadowableLayer* aCanvas); - /** - * Notify the shadow manager that a buffer has been created for the - * specificed layer. |aInitialFrontSurface| is one of the newly - * created, transparent black buffers for the layer; the "real" - * layer holds on to the other as its back buffer. We send it - * across on buffer creation to avoid special cases in the buffer - * swapping logic for Painted*() operations. - * - * It is expected that Created*Buffer() will be followed by a - * Painted*Buffer() in the same transaction, so that - * |aInitialFrontBuffer| is never actually drawn to screen. It is - * OK if it is drawn though. - */ - /** - * |aBufferRect| is the screen rect covered by |aInitialFrontBuffer|. - */ - void CreatedThebesBuffer(ShadowableLayer* aThebes, - const nsIntRegion& aFrontValidRegion, - const nsIntRect& aBufferRect, - const SurfaceDescriptor& aInitialFrontBuffer); - - /** - * The specified layer is destroying its buffers. - * |aBackBufferToDestroy| is deallocated when this transaction is - * posted to the parent. During the parent-side transaction, the - * shadow is told to destroy its front buffer. This can happen when - * a new front/back buffer pair have been created because of a layer - * resize, e.g. - */ - void DestroyedThebesBuffer(ShadowableLayer* aThebes, - const SurfaceDescriptor& aBackBufferToDestroy); - /** * At least one attribute of |aMutant| has changed, and |aMutant| * needs to sync to its shadow layer. This initial implementation @@ -506,15 +474,6 @@ class ShadowThebesLayer : public ShadowLayer, public ThebesLayer { public: - /** - * CONSTRUCTION PHASE ONLY - * - * Override the front buffer and its valid region with the specified - * values. This is called when a new buffer has been created. - */ - virtual void SetFrontBuffer(const OptionalThebesBuffer& aNewFront, - const nsIntRegion& aValidRegion) = 0; - virtual void InvalidateRegion(const nsIntRegion& aRegion) { NS_RUNTIMEABORT("ShadowThebesLayers can't fill invalidated regions"); @@ -538,7 +497,7 @@ public: */ virtual void Swap(const ThebesBuffer& aNewFront, const nsIntRegion& aUpdatedRegion, - ThebesBuffer* aNewBack, nsIntRegion* aNewBackValidRegion, + OptionalThebesBuffer* aNewBack, nsIntRegion* aNewBackValidRegion, OptionalThebesBuffer* aReadOnlyFront, nsIntRegion* aFrontUpdatedRegion) = 0; /** diff --git a/gfx/layers/ipc/ShadowLayersParent.cpp b/gfx/layers/ipc/ShadowLayersParent.cpp index 625183b40122..58ee6a524814 100644 --- a/gfx/layers/ipc/ShadowLayersParent.cpp +++ b/gfx/layers/ipc/ShadowLayersParent.cpp @@ -203,29 +203,7 @@ ShadowLayersParent::RecvUpdate(const InfallibleTArray& cset, AsShadowLayer(edit.get_OpCreateCanvasLayer())->Bind(layer); break; } - case Edit::TOpCreateThebesBuffer: { - MOZ_LAYERS_LOG(("[ParentSide] CreateThebesBuffer")); - const OpCreateThebesBuffer& otb = edit.get_OpCreateThebesBuffer(); - ShadowThebesLayer* thebes = static_cast( - AsShadowLayer(otb)->AsLayer()); - - thebes->SetFrontBuffer(otb.initialFront(), otb.frontValidRegion()); - - break; - } - case Edit::TOpDestroyThebesFrontBuffer: { - MOZ_LAYERS_LOG(("[ParentSide] DestroyThebesFrontBuffer")); - - const OpDestroyThebesFrontBuffer& odfb = - edit.get_OpDestroyThebesFrontBuffer(); - ShadowThebesLayer* thebes = static_cast( - AsShadowLayer(odfb)->AsLayer()); - - thebes->DestroyFrontBuffer(); - - break; - } // Attributes case Edit::TOpSetLayerAttributes: { MOZ_LAYERS_LOG(("[ParentSide] SetLayerAttributes")); @@ -339,7 +317,7 @@ ShadowLayersParent::RecvUpdate(const InfallibleTArray& cset, static_cast(shadow->AsLayer()); const ThebesBuffer& newFront = op.newFrontBuffer(); - ThebesBuffer newBack; + OptionalThebesBuffer newBack; nsIntRegion newValidRegion; OptionalThebesBuffer readonlyFront; nsIntRegion frontUpdatedRegion; diff --git a/gfx/layers/opengl/ContainerLayerOGL.cpp b/gfx/layers/opengl/ContainerLayerOGL.cpp index ec9414e1384f..55741ac93e8a 100644 --- a/gfx/layers/opengl/ContainerLayerOGL.cpp +++ b/gfx/layers/opengl/ContainerLayerOGL.cpp @@ -175,7 +175,6 @@ ContainerRender(Container* aContainer, float opacity = aContainer->GetEffectiveOpacity(); const gfx3DMatrix& transform = aContainer->GetEffectiveTransform(); bool needsFramebuffer = aContainer->UseIntermediateSurface(); - gfxMatrix contTransform; if (needsFramebuffer) { LayerManagerOGL::InitMode mode = LayerManagerOGL::InitModeClear; nsIntRect framebufferRect = visibleRect; @@ -213,19 +212,16 @@ ContainerRender(Container* aContainer, frameBuffer = aPreviousFrameBuffer; aContainer->mSupportsComponentAlphaChildren = (aContainer->GetContentFlags() & Layer::CONTENT_OPAQUE) || (aContainer->GetParent() && aContainer->GetParent()->SupportsComponentAlphaChildren()); -#ifdef DEBUG - bool is2d = -#endif - transform.Is2D(&contTransform); - NS_ASSERTION(is2d, "Transform must be 2D"); } + nsAutoTArray children; + aContainer->SortChildrenBy3DZOrder(children); + /** * Render this container's contents. */ - for (LayerOGL* layerToRender = aContainer->GetFirstChildOGL(); - layerToRender != nsnull; - layerToRender = GetNextSibling(layerToRender)) { + for (PRUint32 i = 0; i < children.Length(); i++) { + LayerOGL* layerToRender = static_cast(children.ElementAt(i)->ImplData()); if (layerToRender->GetLayer()->GetEffectiveVisibleRegion().IsEmpty()) { continue; diff --git a/gfx/layers/opengl/ThebesLayerOGL.cpp b/gfx/layers/opengl/ThebesLayerOGL.cpp index ea779165bfe1..97204dbf59cb 100644 --- a/gfx/layers/opengl/ThebesLayerOGL.cpp +++ b/gfx/layers/opengl/ThebesLayerOGL.cpp @@ -878,31 +878,18 @@ ShadowThebesLayerOGL::ShadowThebesLayerOGL(LayerManagerOGL *aManager) ShadowThebesLayerOGL::~ShadowThebesLayerOGL() {} -void -ShadowThebesLayerOGL::SetFrontBuffer(const OptionalThebesBuffer& aNewFront, - const nsIntRegion& aValidRegion) -{ - if (mDestroyed) { - return; - } - - if (!mBuffer) { - mBuffer = new ShadowBufferOGL(this); - } - - NS_ASSERTION(OptionalThebesBuffer::Tnull_t == aNewFront.type(), - "Only one system-memory buffer expected"); -} - void ShadowThebesLayerOGL::Swap(const ThebesBuffer& aNewFront, const nsIntRegion& aUpdatedRegion, - ThebesBuffer* aNewBack, + OptionalThebesBuffer* aNewBack, nsIntRegion* aNewBackValidRegion, OptionalThebesBuffer* aReadOnlyFront, nsIntRegion* aFrontUpdatedRegion) { - if (!mDestroyed && mBuffer) { + if (!mDestroyed) { + if (!mBuffer) { + mBuffer = new ShadowBufferOGL(this); + } nsRefPtr surf = ShadowLayerForwarder::OpenDescriptor(aNewFront.buffer()); mBuffer->Upload(surf, aUpdatedRegion, aNewFront.rect(), aNewFront.rotation()); } diff --git a/gfx/layers/opengl/ThebesLayerOGL.h b/gfx/layers/opengl/ThebesLayerOGL.h index 92f114ae3640..0f4240aab212 100644 --- a/gfx/layers/opengl/ThebesLayerOGL.h +++ b/gfx/layers/opengl/ThebesLayerOGL.h @@ -91,12 +91,9 @@ public: ShadowThebesLayerOGL(LayerManagerOGL *aManager); virtual ~ShadowThebesLayerOGL(); - // ShadowThebesLayer impl - virtual void SetFrontBuffer(const OptionalThebesBuffer& aNewFront, - const nsIntRegion& aValidRegion); virtual void Swap(const ThebesBuffer& aNewFront, const nsIntRegion& aUpdatedRegion, - ThebesBuffer* aNewBack, nsIntRegion* aNewBackValidRegion, + OptionalThebesBuffer* aNewBack, nsIntRegion* aNewBackValidRegion, OptionalThebesBuffer* aReadOnlyFront, nsIntRegion* aFrontUpdatedRegion); virtual void DestroyFrontBuffer(); diff --git a/gfx/thebes/Makefile.in b/gfx/thebes/Makefile.in index 6eaeacf9a748..f518de16c53d 100644 --- a/gfx/thebes/Makefile.in +++ b/gfx/thebes/Makefile.in @@ -34,6 +34,7 @@ EXPORTS = \ gfxPoint.h \ gfxPoint3D.h \ gfxPointH3D.h \ + gfxQuad.h \ gfxQuaternion.h \ gfxRect.h \ gfxSkipChars.h \ diff --git a/gfx/thebes/gfx3DMatrix.cpp b/gfx/thebes/gfx3DMatrix.cpp index e8a139e3301e..c22a5a636f72 100644 --- a/gfx/thebes/gfx3DMatrix.cpp +++ b/gfx/thebes/gfx3DMatrix.cpp @@ -667,6 +667,22 @@ gfx3DMatrix::TransformBounds(const gfxRect& rect) const return gfxRect(min_x, min_y, max_x - min_x, max_y - min_y); } +gfxQuad +gfx3DMatrix::TransformRect(const gfxRect& aRect) const +{ + gfxPoint points[4]; + + points[0] = Transform(aRect.TopLeft()); + points[1] = Transform(gfxPoint(aRect.X() + aRect.Width(), aRect.Y())); + points[2] = Transform(gfxPoint(aRect.X() + aRect.Width(), + aRect.Y() + aRect.Height())); + points[3] = Transform(gfxPoint(aRect.X(), aRect.Y() + aRect.Height())); + + + // Could this ever result in lines that intersect? I don't think so. + return gfxQuad(points[0], points[1], points[2], points[3]); +} + bool gfx3DMatrix::Is2D() const { @@ -714,6 +730,19 @@ gfx3DMatrix::CanDraw2D(gfxMatrix* aMatrix) const return PR_TRUE; } +gfx3DMatrix& +gfx3DMatrix::ProjectTo2D() +{ + _31 = 0.0f; + _32 = 0.0f; + _13 = 0.0f; + _23 = 0.0f; + _33 = 1.0f; + _43 = 0.0f; + _34 = 0.0f; + return *this; +} + gfxPoint gfx3DMatrix::ProjectPoint(const gfxPoint& aPoint) const { // Define a ray of the form P + Ut where t is a real number diff --git a/gfx/thebes/gfx3DMatrix.h b/gfx/thebes/gfx3DMatrix.h index 20cad5b1ae5d..4cb4dde501fe 100644 --- a/gfx/thebes/gfx3DMatrix.h +++ b/gfx/thebes/gfx3DMatrix.h @@ -43,6 +43,7 @@ #include #include #include +#include /** * This class represents a 3D transformation. The matrix is laid @@ -120,6 +121,12 @@ public: */ bool CanDraw2D(gfxMatrix* aMatrix = nsnull) const; + /** + * Converts the matrix to one that doesn't modify the z coordinate of points, + * but leaves the rest of the transformation unchanged. + */ + gfx3DMatrix& ProjectTo2D(); + /** * Returns true if the matrix is the identity matrix. The most important * property we require is that gfx3DMatrix().IsIdentity() returns true. @@ -247,6 +254,9 @@ public: */ gfxRect TransformBounds(const gfxRect& rect) const; + + gfxQuad TransformRect(const gfxRect& aRect) const; + /** * Transforms a 3D vector according to this matrix. */ diff --git a/gfx/thebes/gfxQuad.h b/gfx/thebes/gfxQuad.h new file mode 100644 index 000000000000..b8d5d4fa6488 --- /dev/null +++ b/gfx/thebes/gfxQuad.h @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Corporation code. + * + * The Initial Developer of the Original Code is Oracle Corporation. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Matt Woodrow + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef GFX_QUAD_H +#define GFX_QUAD_H + +#include "nsMathUtils.h" +#include "mozilla/gfx/BaseSize.h" +#include "mozilla/gfx/BasePoint.h" +#include "nsSize.h" +#include "nsPoint.h" + +#include "gfxTypes.h" + +static PRBool SameSideOfLine(const gfxPoint& aPoint1, const gfxPoint& aPoint2, const gfxPoint& aTest, const gfxPoint& aRef) +{ + // Solve the equation y - aPoint1.y - ((aPoint2.y - aPoint1.y)/(aPoint2.x - aPoint1.x))(x - aPoint1.x) for both test and ref + + gfxFloat deltaY = (aPoint2.y - aPoint1.y); + gfxFloat deltaX = (aPoint2.x - aPoint1.x); + + gfxFloat test = deltaX * (aTest.y - aPoint1.y) - deltaY * (aTest.x - aPoint1.x); + gfxFloat ref = deltaX * (aRef.y - aPoint1.y) - deltaY * (aRef.x - aPoint1.x); + + // If both results have the same sign, then we're on the correct side of the line. + // 0 (on the line) is always considered in. + + if ((test >= 0 && ref >= 0) || (test <= 0 && ref <= 0)) + return PR_TRUE; + return PR_FALSE; +} + +struct THEBES_API gfxQuad { + gfxQuad(const gfxPoint& aOne, const gfxPoint& aTwo, const gfxPoint& aThree, const gfxPoint& aFour) + { + mPoints[0] = aOne; + mPoints[1] = aTwo; + mPoints[2] = aThree; + mPoints[3] = aFour; + } + + PRBool Contains(const gfxPoint& aPoint) + { + return (SameSideOfLine(mPoints[0], mPoints[1], aPoint, mPoints[2]) && + SameSideOfLine(mPoints[1], mPoints[2], aPoint, mPoints[3]) && + SameSideOfLine(mPoints[2], mPoints[3], aPoint, mPoints[0]) && + SameSideOfLine(mPoints[3], mPoints[0], aPoint, mPoints[1])); + } + + gfxPoint mPoints[4]; +}; + +#endif /* GFX_QUAD_H */ diff --git a/intl/hyphenation/public/nsHyphenationManager.h b/intl/hyphenation/public/nsHyphenationManager.h index 9bf98e62f412..f54e8da7b97d 100644 --- a/intl/hyphenation/public/nsHyphenationManager.h +++ b/intl/hyphenation/public/nsHyphenationManager.h @@ -41,9 +41,11 @@ #include "nsInterfaceHashtable.h" #include "nsRefPtrHashtable.h" #include "nsHashKeys.h" +#include "mozilla/Omnijar.h" class nsHyphenator; class nsIAtom; +class nsIURI; class nsHyphenationManager { @@ -61,11 +63,12 @@ private: protected: void LoadPatternList(); + void LoadPatternListFromOmnijar(mozilla::Omnijar::Type aType); void LoadPatternListFromDir(nsIFile *aDir); void LoadAliases(); nsInterfaceHashtable mHyphAliases; - nsInterfaceHashtable mPatternFiles; + nsInterfaceHashtable mPatternFiles; nsRefPtrHashtable mHyphenators; static nsHyphenationManager *sInstance; diff --git a/intl/hyphenation/public/nsHyphenator.h b/intl/hyphenation/public/nsHyphenator.h index a3bd114c283e..fa7c9a6e97a7 100644 --- a/intl/hyphenation/public/nsHyphenator.h +++ b/intl/hyphenation/public/nsHyphenator.h @@ -42,12 +42,13 @@ #include "nsString.h" #include "nsTArray.h" +class nsIURI; class nsIUGenCategory; class nsHyphenator { public: - nsHyphenator(nsIFile *aFile); + nsHyphenator(nsIURI *aURI); NS_INLINE_DECL_REFCOUNTING(nsHyphenator) diff --git a/intl/hyphenation/src/nsHyphenationManager.cpp b/intl/hyphenation/src/nsHyphenationManager.cpp index 5e43e92ad94d..a96a8ca41260 100644 --- a/intl/hyphenation/src/nsHyphenationManager.cpp +++ b/intl/hyphenation/src/nsHyphenationManager.cpp @@ -39,12 +39,15 @@ #include "nsHyphenator.h" #include "nsIAtom.h" #include "nsIFile.h" +#include "nsIURI.h" #include "nsIProperties.h" #include "nsISimpleEnumerator.h" #include "nsIDirectoryEnumerator.h" #include "nsDirectoryServiceDefs.h" +#include "nsNetUtil.h" #include "nsUnicharUtils.h" #include "mozilla/Preferences.h" +#include "nsZipArchive.h" using namespace mozilla; @@ -89,20 +92,20 @@ nsHyphenationManager::GetHyphenator(nsIAtom *aLocale) if (hyph) { return hyph.forget(); } - nsCOMPtr file = mPatternFiles.Get(aLocale); - if (!file) { + nsCOMPtr uri = mPatternFiles.Get(aLocale); + if (!uri) { nsCOMPtr alias = mHyphAliases.Get(aLocale); if (alias) { mHyphenators.Get(alias, getter_AddRefs(hyph)); if (hyph) { return hyph.forget(); } - file = mPatternFiles.Get(alias); - if (file) { + uri = mPatternFiles.Get(alias); + if (uri) { aLocale = alias; } } - if (!file) { + if (!uri) { // In the case of a locale such as "de-DE-1996", we try replacing // successive trailing subtags with "-*" to find fallback patterns, // so "de-DE-1996" -> "de-DE-*" (and then recursively -> "de-*") @@ -120,14 +123,14 @@ nsHyphenationManager::GetHyphenator(nsIAtom *aLocale) } } } - hyph = new nsHyphenator(file); + hyph = new nsHyphenator(uri); if (hyph->IsValid()) { mHyphenators.Put(aLocale, hyph); return hyph.forget(); } #ifdef DEBUG nsCString msg; - file->GetNativePath(msg); + uri->GetSpec(msg); msg.Insert("failed to load patterns from ", 0); NS_WARNING(msg.get()); #endif @@ -140,15 +143,17 @@ nsHyphenationManager::LoadPatternList() { mPatternFiles.Clear(); mHyphenators.Clear(); - - nsresult rv; - + + LoadPatternListFromOmnijar(Omnijar::GRE); + LoadPatternListFromOmnijar(Omnijar::APP); + nsCOMPtr dirSvc = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); if (!dirSvc) { return; } - + + nsresult rv; nsCOMPtr greDir; rv = dirSvc->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), getter_AddRefs(greDir)); @@ -156,7 +161,7 @@ nsHyphenationManager::LoadPatternList() greDir->AppendNative(NS_LITERAL_CSTRING("hyphenation")); LoadPatternListFromDir(greDir); } - + nsCOMPtr appDir; rv = dirSvc->Get(NS_XPCOM_CURRENT_PROCESS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(appDir)); @@ -169,17 +174,72 @@ nsHyphenationManager::LoadPatternList() } } +void +nsHyphenationManager::LoadPatternListFromOmnijar(Omnijar::Type aType) +{ + nsCString base; + nsresult rv = Omnijar::GetURIString(aType, base); + if (NS_FAILED(rv)) { + return; + } + + nsZipArchive *zip = Omnijar::GetReader(aType); + if (!zip) { + return; + } + + nsZipFind *find; + zip->FindInit("hyphenation/hyph_*.dic", &find); + if (!find) { + return; + } + + const char *result; + PRUint16 len; + while (NS_SUCCEEDED(find->FindNext(&result, &len))) { + nsCString uriString(base); + uriString.Append(result, len); + nsCOMPtr uri; + rv = NS_NewURI(getter_AddRefs(uri), uriString); + if (NS_FAILED(rv)) { + continue; + } + nsCString locale; + rv = uri->GetPath(locale); + if (NS_FAILED(rv)) { + continue; + } + ToLowerCase(locale); + locale.SetLength(locale.Length() - 4); // strip ".dic" + locale.Cut(0, locale.RFindChar('/') + 1); // strip directory + if (StringBeginsWith(locale, NS_LITERAL_CSTRING("hyph_"))) { + locale.Cut(0, 5); + } + for (PRUint32 i = 0; i < locale.Length(); ++i) { + if (locale[i] == '_') { + locale.Replace(i, 1, '-'); + } + } + nsCOMPtr localeAtom = do_GetAtom(locale); + if (NS_SUCCEEDED(rv)) { + mPatternFiles.Put(localeAtom, uri); + } + } + + delete find; +} + void nsHyphenationManager::LoadPatternListFromDir(nsIFile *aDir) { nsresult rv; - + bool check = false; rv = aDir->Exists(&check); if (NS_FAILED(rv) || !check) { return; } - + rv = aDir->IsDirectory(&check); if (NS_FAILED(rv) || !check) { return; @@ -190,12 +250,12 @@ nsHyphenationManager::LoadPatternListFromDir(nsIFile *aDir) if (NS_FAILED(rv)) { return; } - + nsCOMPtr files(do_QueryInterface(e)); if (!files) { return; } - + nsCOMPtr file; while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(file))) && file){ nsAutoString dictName; @@ -219,7 +279,11 @@ nsHyphenationManager::LoadPatternListFromDir(nsIFile *aDir) NS_ConvertUTF16toUTF8(dictName).get()); #endif nsCOMPtr localeAtom = do_GetAtom(locale); - mPatternFiles.Put(localeAtom, file); + nsCOMPtr uri; + nsresult rv = NS_NewFileURI(getter_AddRefs(uri), file); + if (NS_SUCCEEDED(rv)) { + mPatternFiles.Put(localeAtom, uri); + } } } diff --git a/intl/hyphenation/src/nsHyphenator.cpp b/intl/hyphenation/src/nsHyphenator.cpp index 05f4e8319541..1a3d94a1272e 100644 --- a/intl/hyphenation/src/nsHyphenator.cpp +++ b/intl/hyphenation/src/nsHyphenator.cpp @@ -40,22 +40,22 @@ #include "nsUTF8Utils.h" #include "nsIUGenCategory.h" #include "nsUnicharUtilCIID.h" -#include "nsNetUtil.h" +#include "nsIURI.h" #include "hyphen.h" -nsHyphenator::nsHyphenator(nsIFile *aFile) +nsHyphenator::nsHyphenator(nsIURI *aURI) : mDict(nsnull) { - nsCString urlSpec; - nsresult rv = NS_GetURLSpecFromFile(aFile, urlSpec); + nsCString uriSpec; + nsresult rv = aURI->GetSpec(uriSpec); if (NS_FAILED(rv)) { return; } - mDict = hnj_hyphen_load(urlSpec.get()); + mDict = hnj_hyphen_load(uriSpec.get()); #ifdef DEBUG if (mDict) { - printf("loaded hyphenation patterns from %s\n", urlSpec.get()); + printf("loaded hyphenation patterns from %s\n", uriSpec.get()); } #endif mCategories = do_GetService(NS_UNICHARCATEGORY_CONTRACTID, &rv); diff --git a/js/ductwork/Makefile.in b/js/ductwork/Makefile.in deleted file mode 100644 index 73e048aab419..000000000000 --- a/js/ductwork/Makefile.in +++ /dev/null @@ -1,49 +0,0 @@ -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is the Mozilla Browser code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 2011 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Jason Orendorff -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(topsrcdir)/config/config.mk - -PARALLEL_DIRS += \ - debugger \ - $(NULL) - -include $(topsrcdir)/config/rules.mk diff --git a/js/src/jit-test/tests/basic/bug690292.js b/js/src/jit-test/tests/basic/bug690292.js new file mode 100644 index 000000000000..06647272799a --- /dev/null +++ b/js/src/jit-test/tests/basic/bug690292.js @@ -0,0 +1,12 @@ + +done = false; +try { + function x() {} + print(this.watch("d", Object.create)) + var d = +} catch (e) {} +try { + eval("d = ''") + done = true; +} catch (e) {} +assertEq(done, false); diff --git a/js/src/jit-test/tests/basic/splice-675164.js b/js/src/jit-test/tests/basic/splice-675164.js new file mode 100644 index 000000000000..10d0d97887f5 --- /dev/null +++ b/js/src/jit-test/tests/basic/splice-675164.js @@ -0,0 +1,15 @@ +function NPList() {} +NPList.prototype = new Array; + +var list = new NPList(); +list.push('a'); + +var cut = list.splice(0, 1); + +assertEq(cut[0], 'a'); +assertEq(cut.length, 1); +assertEq(list.length, 0); + +var desc = Object.getOwnPropertyDescriptor(list, "0"); +assertEq(desc, undefined); +assertEq("0" in list, false); diff --git a/js/src/jit-test/tests/basic/splice-call-plain-object-590780.js b/js/src/jit-test/tests/basic/splice-call-plain-object-590780.js new file mode 100644 index 000000000000..4a0dc5d1592d --- /dev/null +++ b/js/src/jit-test/tests/basic/splice-call-plain-object-590780.js @@ -0,0 +1,8 @@ +var o = { 0: 1, 1: 2, 2: 3, length: 3 }; +Array.prototype.splice.call(o, 0, 1); + +assertEq(o[0], 2); +assertEq(o[1], 3); +assertEq(Object.getOwnPropertyDescriptor(o, 2), undefined); +assertEq("2" in o, false); +assertEq(o.length, 2); diff --git a/js/src/jit-test/tests/basic/splice-check-steps.js b/js/src/jit-test/tests/basic/splice-check-steps.js new file mode 100644 index 000000000000..5f5e04fa0650 --- /dev/null +++ b/js/src/jit-test/tests/basic/splice-check-steps.js @@ -0,0 +1,327 @@ +/* + * Check the order of splice's internal operations, because the ordering is + * visible externally. + */ + +function handlerMaker(obj, expected_exceptions) { + var order = []; + function note(trap, name) + { + order.push(trap + '-' + name); + if (expected_exceptions[trap] === name) { + throw ("fail"); + } + } + + return [{ + /* this is the only trap we care about */ + delete: function(name) { + note("del", name); + return delete obj[name]; + }, + + // Fundamental traps + getOwnPropertyDescriptor: function(name) { + var desc = Object.getOwnPropertyDescriptor(obj, name); + // a trapping proxy's properties must always be configurable + if (desc !== undefined) + desc.configurable = true; + return desc; + }, + getPropertyDescriptor: function(name) { + var desc = Object.getPropertyDescriptor(obj, name); // not in ES5 + // a trapping proxy's properties must always be configurable + if (desc !== undefined) + desc.configurable = true; + return desc; + }, + getOwnPropertyNames: function() { + return Object.getOwnPropertyNames(obj); + }, + getPropertyNames: function() { + return Object.getPropertyNames(obj); // not in ES5 + }, + defineProperty: function(name, desc) { + note("def", name); + Object.defineProperty(obj, name, desc); + }, + fix: function() { + if (Object.isFrozen(obj)) { + return Object.getOwnPropertyNames(obj).map(function(name) { + return Object.getOwnPropertyDescriptor(obj, name); + }); + } + // As long as obj is not frozen, the proxy won't allow itself to be fixed + return undefined; // will cause a TypeError to be thrown + }, + + // derived traps + has: function(name) { + note("has", name); + return name in obj; + }, + hasOwn: function(name) { return Object.prototype.hasOwnProperty.call(obj, name); }, + get: function(receiver, name) { + note("get", name); + return obj[name]; + }, + set: function(receiver, name, val) { + note("set", name); + obj[name] = val; + return true; // bad behavior when set fails in non-strict mode + }, + enumerate: function() { + var result = []; + for (name in obj) + result.push(name); + return result; + }, + keys: function() { return Object.keys(obj) } + }, order]; +} + +// arr: the array to splice +// expected_order: the expected order of operations on arr, stringified +function check_splice_proxy(arr, expected_order, expected_exceptions, expected_array, expected_result) { + print (arr); + var [handler, store] = handlerMaker(arr, expected_exceptions); + var proxy = Proxy.create(handler); + + try { + var args = Array.prototype.slice.call(arguments, 5); + var result = Array.prototype.splice.apply(proxy, args); + assertEq(Object.keys(expected_exceptions).length, 0); + } catch (e) { + assertEq(Object.keys(expected_exceptions).length > 0, true); + } + + // check the order of the property accesses, etc + assertEq(store.toString(), expected_order); + + // The deleted elements are returned in an object that's always an Array. + assertEq(Array.isArray(result) || result === undefined, true); + + // check the return value + for (var i in expected_result) { + assertEq(result[i], expected_result[i]); + } + for (var i in result) { + assertEq(result[i], expected_result[i]); + } + + // check the value of arr + for (var i in expected_array) { + assertEq(arr[i], expected_array[i]); + } + for (var i in arr) { + assertEq(arr[i], expected_array[i]); + } + + return result; +} + +// Shrinking array +check_splice_proxy( + [10,1,2,3,4,5], + "get-length," + + "has-0,get-0,has-1,get-1,has-2,get-2," + + "has-3,get-3,set-0,has-4,get-4,set-1,has-5,get-5,set-2," + + "del-5,del-4,del-3," + + "set-length", + {}, + [3,4,5], + [10,1,2], + 0, 3 +); + +// Growing array +check_splice_proxy( + [11,1,2,3,4,5], + "get-length," + + "has-0,get-0,has-1,get-1,has-2,get-2," + + "has-5,get-5,set-9,has-4,get-4,set-8,has-3,get-3,set-7," + + "set-0,set-1,set-2,set-3,set-4,set-5,set-6," + + "set-length", + {}, + [9,9,9,9,9,9,9,3,4,5], + [11,1,2], + 0, 3, 9, 9, 9, 9, 9, 9, 9 +); + +// Same sized array +check_splice_proxy( + [12,1,2,3,4,5], + "get-length," + + "has-0,get-0,has-1,get-1,has-2,get-2," + + "set-0,set-1,set-2," + + "set-length", + {}, + [9,9,9,3,4,5], + [12,1,2], + 0, 3, 9, 9, 9 +); + + +/* + * Check that if we fail at a particular step in the algorithm, we don't + * continue with the algorithm beyond that step. + */ + + +// Step 3: fail when getting length +check_splice_proxy( + [13,1,2,3,4,5], + "get-length", + {get: 'length'}, + [13,1,2,3,4,5], + undefined, + 0, 3, 9, 9, 9 +); + +// Step 9b: fail when [[HasProperty]] +check_splice_proxy( + [14,1,2,3,4,5], + "get-length," + + "has-0,get-0,has-1", + {has: '1'}, + [14,1,2,3,4,5], + undefined, + 0, 3, 9, 9, 9 +); + +// Step 9c(i): fail when [[Get]] +check_splice_proxy( + [15,1,2,3,4,5], + "get-length," + + "has-0,get-0,has-1,get-1", + {get: '1'}, + [15,1,2,3,4,5], + undefined, + 0, 3, 9, 9, 9 +); + +// Step 12b(iii): fail when [[HasProperty]] +check_splice_proxy( + [16,1,2,3,4,5], + "get-length," + + "has-0,get-0,has-1,get-1,has-2,get-2," + + "has-3,get-3,set-0,has-4", + {has: '4'}, + [3,1,2,3,4,5], + undefined, + 0, 3 +); + + +// Step 12b(iv)1: fail when [[Get]] +check_splice_proxy( + [17,1,2,3,4,5], + "get-length," + + "has-0,get-0,has-1,get-1,has-2,get-2," + + "has-3,get-3,set-0,has-4,get-4", + {get: '4'}, + [3,1,2,3,4,5], + undefined, + 0, 3 +); + + +// Step 12b(iv)2: fail when [[Put]] +check_splice_proxy( + [18,1,2,3,4,5], + "get-length," + + "has-0,get-0,has-1,get-1,has-2,get-2," + + "has-3,get-3,set-0,has-4,get-4,set-1", + {set: '1'}, + [3,1,2,3,4,5], + undefined, + 0, 3 +); + +// Step 12b(v)1: fail when [[Delete]] +check_splice_proxy( + [19,1,2,3,,5], + "get-length," + + "has-0,get-0,has-1,get-1,has-2,get-2," + + "has-3,get-3,set-0,has-4,del-1", + {del: '1'}, + [3,1,2,3,,5], + undefined, + 0, 3 +); + +// Step 12d(i): fail when [[Delete]] +check_splice_proxy( + [20,1,2,3,4,5], + "get-length," + + "has-0,get-0,has-1,get-1,has-2,get-2," + + "has-3,get-3,set-0,has-4,get-4,set-1,has-5,get-5,set-2," + + "del-5,del-4", + {del: '4'}, + [3,4,5,3,4], + undefined, + 0, 3 +); + +// Step 13b(iii): fail when [[HasProperty]] +check_splice_proxy( + [21,1,2,3,4,5], + "get-length," + + "has-0,get-0,has-1,get-1,has-2,get-2," + + "has-5,get-5,set-8,has-4", + {has: '4'}, + [21,1,2,3,4,5,,,5], + undefined, + 0, 3, 9,9,9,9,9,9 +); + + +// Step 13b(iv)1: fail when [[Get]] +check_splice_proxy( + [22,1,2,3,4,5], + "get-length," + + "has-0,get-0,has-1,get-1,has-2,get-2," + + "has-5,get-5,set-8,has-4,get-4", + {get: '4'}, + [22,1,2,3,4,5,,,5], + undefined, + 0, 3, 9,9,9,9,9,9 +); + + +// Step 13b(iv)2: fail when [[Put]] +check_splice_proxy( + [23,1,2,3,4,5], + "get-length," + + "has-0,get-0,has-1,get-1,has-2,get-2," + + "has-5,get-5,set-8,has-4,get-4,set-7", + {set: '7'}, + [23,1,2,3,4,5,,,5], + undefined, + 0, 3, 9,9,9,9,9,9 +); + +// Step 13b(v)1: fail when [[Delete]] +check_splice_proxy( + [24,1,2,3,,5], + "get-length," + + "has-0,get-0,has-1,get-1,has-2,get-2," + + "has-5,get-5,set-8,has-4,del-7", + {del: '7'}, + [24,1,2,3,,5,,,5], + undefined, + 0, 3, 9,9,9,9,9,9 +); + +// Step 15b: fail when [[Put]] +check_splice_proxy( + [25,1,2,3,4,5], + "get-length," + + "has-0,get-0,has-1,get-1,has-2,get-2," + + "has-5,get-5,set-8,has-4,get-4,set-7,has-3,get-3,set-6," + + "set-0,set-1,set-2", + {set: '2'}, + [9,9,2,3,4,5,3,4,5], + undefined, + 0, 3, 9,9,9,9,9,9 +); diff --git a/js/src/jit-test/tests/basic/splice-delete-non-configurable-during-shrink.js b/js/src/jit-test/tests/basic/splice-delete-non-configurable-during-shrink.js new file mode 100644 index 000000000000..93dddb5cc6ec --- /dev/null +++ b/js/src/jit-test/tests/basic/splice-delete-non-configurable-during-shrink.js @@ -0,0 +1,26 @@ +/* Test that splice causing deletion of a non-configurable property stops at exactly step 12(v) of ES5 15.4.4.12 */ + +var O = [1,2,3,4,5,6]; +var A = undefined; +Object.defineProperty(O, 3, { configurable: false }); + +try +{ + A = O.splice(0, 6); + throw new Error("didn't throw, returned " + A); +} +catch (e) +{ + assertEq(e instanceof TypeError, true, + "deleting O[3] should have caused a TypeError"); +} + +assertEq(O.length, 6); // setting length not reached +assertEq(A, undefined); // return value not reached + +assertEq(O[5], undefined); // deletion reached +assertEq(O[4], undefined); // deletion reached +assertEq(O[3], 4); // deletion caused exception +assertEq(O[2], 3); // deletion not reached +assertEq(O[1], 2); // deletion not reached +assertEq(O[0], 1); // deletion not reached diff --git a/js/src/jit-test/tests/basic/splice-fail-step-16.js b/js/src/jit-test/tests/basic/splice-fail-step-16.js new file mode 100644 index 000000000000..e4ea35ada945 --- /dev/null +++ b/js/src/jit-test/tests/basic/splice-fail-step-16.js @@ -0,0 +1,29 @@ +// |jit-test| error: InternalError +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Remove when [].length is redefinable! + +/* Test that arrays resize normally during splice, even if .length is non-writable. */ + +var arr = [1, 2, 3, 4, 5, 6]; + +Object.defineProperty(arr, "length", {writable: false}); + +try +{ + var removed = arr.splice(3, 3, 9, 9, 9, 9); + throw new Error("splice didn't throw, returned [" + removed + "]"); +} +catch (e) +{ + assertEq(e instanceof TypeError, true, + "should have thrown a TypeError, instead threw " + e); +} + +// The exception should happen in step 16, which means we've already removed the array elements. +assertEq(arr[0], 1); +assertEq(arr[1], 2); +assertEq(arr[2], 3); +assertEq(arr[3], 9); +assertEq(arr[4], 9); +assertEq(arr[5], 9); +assertEq(arr[6], 9); +assertEq(arr.length, 6); diff --git a/js/src/jit-test/tests/basic/splice-huge-array-finishes.js b/js/src/jit-test/tests/basic/splice-huge-array-finishes.js new file mode 100644 index 000000000000..d894e508880a --- /dev/null +++ b/js/src/jit-test/tests/basic/splice-huge-array-finishes.js @@ -0,0 +1,15 @@ +// Making the array huge and sparse shouldn't leave us iterating through the entire array. +// But it does, sadly. Disable, because it takes too long. +if (0) { + var arr = [1, 2, 3, 4, 5, 6, 7, 8]; + arr.length = Math.pow(2, 32) - 2; + arr.splice(5); // also test overflow + + assertEq(arr.length, 5); + assertEq(arr[0], 1); + assertEq(arr[1], 2); + assertEq(arr[2], 3); + assertEq(arr[3], 4); + assertEq(arr[4], 5); + assertEq(arr[5], undefined); +} diff --git a/js/src/jit-test/tests/basic/splice-on-arguments.js b/js/src/jit-test/tests/basic/splice-on-arguments.js new file mode 100644 index 000000000000..e36522ae0c05 --- /dev/null +++ b/js/src/jit-test/tests/basic/splice-on-arguments.js @@ -0,0 +1,39 @@ +// test whether splice works on arguments + +function splice_args () { + args = arguments; + return Array.prototype.splice.apply(args, [0, 5]); +} + +var args; +var O = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; +var A = splice_args.apply(undefined, O) + +// args: [5, 6, 7, 8, 9] +assertEq(args[0], 5); +assertEq(args[1], 6); +assertEq(args[2], 7); +assertEq(args[3], 8); +assertEq(args[4], 9); +assertEq(args.length, 5); + +// A: [0, 1, 2, 3, 4] +assertEq(A[0], 0); +assertEq(A[1], 1); +assertEq(A[2], 2); +assertEq(A[3], 3); +assertEq(A[4], 4); +assertEq(A.length, 5); + +// O: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] +assertEq(O[0], 0); +assertEq(O[1], 1); +assertEq(O[2], 2); +assertEq(O[3], 3); +assertEq(O[4], 4); +assertEq(O[5], 5); +assertEq(O[6], 6); +assertEq(O[7], 7); +assertEq(O[8], 8); +assertEq(O[9], 9); +assertEq(O.length, 10); diff --git a/js/src/jit-test/tests/basic/splice-throwing-length-getter-668024.js b/js/src/jit-test/tests/basic/splice-throwing-length-getter-668024.js new file mode 100644 index 000000000000..695460b5c8af --- /dev/null +++ b/js/src/jit-test/tests/basic/splice-throwing-length-getter-668024.js @@ -0,0 +1,9 @@ +try +{ + Array.prototype.splice.call({ get length() { throw 'error'; } }); + throw new Error("should have thrown, didn't"); +} +catch (e) +{ + assertEq(e, "error", "wrong error thrown: " + e); +} diff --git a/js/src/jit-test/tests/debug/Frame-script-01.js b/js/src/jit-test/tests/debug/Frame-script-01.js index 74e4c5a4b48b..2b5f73ebe92c 100644 --- a/js/src/jit-test/tests/debug/Frame-script-01.js +++ b/js/src/jit-test/tests/debug/Frame-script-01.js @@ -16,19 +16,11 @@ function ApplyToFrameScript(code, skip, f) { g.eval(code); } -var savedScript; - ApplyToFrameScript('debugger;', 0, function (script) { assertEq(script instanceof Debugger.Script, true); - assertEq(script.live, true); - savedScript = script; }); -assertEq(savedScript.live, false); ApplyToFrameScript("(function () { eval('debugger;'); })();", 0, function (script) { assertEq(script instanceof Debugger.Script, true); - assertEq(script.live, true); - savedScript = script; }); -assertEq(savedScript.live, false); diff --git a/js/src/jit-test/tests/debug/Frame-script-02.js b/js/src/jit-test/tests/debug/Frame-script-02.js index bded68d36b7e..a911a6ec8abb 100644 --- a/js/src/jit-test/tests/debug/Frame-script-02.js +++ b/js/src/jit-test/tests/debug/Frame-script-02.js @@ -16,15 +16,10 @@ function ApplyToFrameScript(code, skip, f) { g.eval(code); } -var savedScript; - ApplyToFrameScript('(function () { debugger; })();', 0, function (script) { assertEq(script instanceof Debugger.Script, true); - assertEq(script.live, true); - savedScript = script; }); -assertEq(savedScript.live, true); // This would be nice, once we can get host call frames: // ApplyToFrameScript("(function () { debugger; }).call(null);", 1, diff --git a/js/src/jit-test/tests/debug/Script-gc-02.js b/js/src/jit-test/tests/debug/Script-gc-02.js index 30d551e60f4f..51746b99dfa1 100644 --- a/js/src/jit-test/tests/debug/Script-gc-02.js +++ b/js/src/jit-test/tests/debug/Script-gc-02.js @@ -10,5 +10,5 @@ assertEq(arr.length, 10); gc(); for (var i = 0; i < arr.length; i++) - assertEq(arr[i].live, true); // XXX FIXME - replace with something that touches the script + assertEq(arr[i].lineCount, 1); diff --git a/js/src/jit-test/tests/debug/Script-gc-03.js b/js/src/jit-test/tests/debug/Script-gc-03.js index c65f36524d5f..f7f5433c2d63 100644 --- a/js/src/jit-test/tests/debug/Script-gc-03.js +++ b/js/src/jit-test/tests/debug/Script-gc-03.js @@ -10,6 +10,6 @@ assertEq(arr.length, 100); gc(g); for (var i = 0; i < arr.length; i++) - assertEq(arr[i].live, true); // XXX FIXME replace with something that touches the script + assertEq(arr[i].lineCount, 1); gc(); diff --git a/js/src/jit-test/tests/debug/breakpoint-03.js b/js/src/jit-test/tests/debug/breakpoint-03.js index b8d12c0326f2..bb2500e8fc4c 100644 --- a/js/src/jit-test/tests/debug/breakpoint-03.js +++ b/js/src/jit-test/tests/debug/breakpoint-03.js @@ -10,7 +10,6 @@ g.eval("function f() { return 2; }"); var s; dbg.onDebuggerStatement = function (frame) { s = frame.eval("f").return.script; }; g.eval("debugger;"); -assertEq(s.live, true); s.setBreakpoint(0, {}); // ok dbg.removeDebuggee(gobj); diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 4561f18fbcc3..40ddbb6975c0 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -648,8 +648,6 @@ JSRuntime::JSRuntime() protoHazardShape(0), gcSystemAvailableChunkListHead(NULL), gcUserAvailableChunkListHead(NULL), - gcEmptyChunkListHead(NULL), - gcEmptyChunkCount(0), gcKeepAtoms(0), gcBytes(0), gcTriggerBytes(0), @@ -695,6 +693,7 @@ JSRuntime::JSRuntime() requestDone(NULL), requestCount(0), gcThread(NULL), + gcHelperThread(this), rtLock(NULL), # ifdef DEBUG rtLockOwner(0), @@ -2792,9 +2791,9 @@ JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key) case JSGC_MODE: return uint32(rt->gcMode); case JSGC_UNUSED_CHUNKS: - return uint32(rt->gcEmptyChunkCount); + return uint32(rt->gcChunkPool.getEmptyCount()); case JSGC_TOTAL_CHUNKS: - return uint32(rt->gcChunkSet.count() + rt->gcEmptyChunkCount); + return uint32(rt->gcChunkSet.count() + rt->gcChunkPool.getEmptyCount()); default: JS_ASSERT(key == JSGC_NUMBER); return rt->gcNumber; @@ -4850,7 +4849,7 @@ CompileUCFunctionForPrincipalsCommon(JSContext *cx, JSObject *obj, chars, length, filename, lineno, version)) { return NULL; } - + if (obj && funAtom && !obj->defineProperty(cx, ATOM_TO_JSID(funAtom), ObjectValue(*fun), NULL, NULL, JSPROP_ENUMERATE)) { diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 242aee13e621..cec22285de5a 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -2855,7 +2855,7 @@ typedef enum JSGCParamKey { /* Select GC mode. */ JSGC_MODE = 6, - /* Number of GC chunks waiting to expire. */ + /* Number of cached empty GC chunks. */ JSGC_UNUSED_CHUNKS = 7, /* Total number of allocated GC chunks. */ diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 3aa522871f57..3b6f3e0e3b0d 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -96,8 +96,12 @@ * SlowArrayClass, but have the same performance characteristics as a dense * array for slot accesses, at some cost in code complexity. */ +#include #include #include + +#include "mozilla/RangedPtr.h" + #include "jstypes.h" #include "jsstdint.h" #include "jsutil.h" @@ -141,6 +145,7 @@ #include "vm/ArgumentsObject-inl.h" #include "vm/Stack-inl.h" +using namespace mozilla; using namespace js; using namespace js::gc; using namespace js::types; @@ -524,7 +529,7 @@ DeleteArrayElement(JSContext *cx, JSObject *obj, jsdouble index, bool strict) if (idx < obj->getDenseArrayInitializedLength()) { obj->markDenseArrayNotPacked(cx); obj->setDenseArrayElement(idx, MagicValue(JS_ARRAY_HOLE)); - if (!js_SuppressDeletedIndexProperties(cx, obj, idx, idx+1)) + if (!js_SuppressDeletedElement(cx, obj, idx)) return -1; } } @@ -1720,7 +1725,7 @@ InitArrayTypes(JSContext *cx, TypeObject *type, const Value *vector, unsigned co } static JSBool -InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, Value *vector, bool updateTypes) +InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, const Value *vector, bool updateTypes) { JS_ASSERT(count <= MAX_ARRAY_INDEX); @@ -1751,14 +1756,14 @@ InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, Valu if (newlen > obj->getArrayLength()) obj->setDenseArrayLength(newlen); - JS_ASSERT(count < uint32(-1) / sizeof(Value)); + JS_ASSERT(count < UINT32_MAX / sizeof(Value)); obj->copyDenseArrayElements(start, vector, count); JS_ASSERT_IF(count != 0, !obj->getDenseArrayElement(newlen - 1).isMagic(JS_ARRAY_HOLE)); return true; } while (false); - Value* end = vector + count; - while (vector != end && start <= MAX_ARRAY_INDEX) { + const Value* end = vector + count; + while (vector < end && start <= MAX_ARRAY_INDEX) { if (!JS_CHECK_OPERATION_LIMIT(cx) || !SetArrayElement(cx, obj, start++, *vector++)) { return JS_FALSE; @@ -1788,6 +1793,7 @@ InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, Valu return JS_TRUE; } +#if 0 static JSBool InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, const Value *vector) { @@ -1820,6 +1826,7 @@ InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, const Value *vector return true; } +#endif /* * Perl-inspired join, reverse, and sort. @@ -2692,154 +2699,223 @@ TryReuseArrayType(JSObject *obj, JSObject *nobj) nobj->setType(obj->type()); } +/* + * Returns true if this is a dense array whose |count| properties starting from + * |startingIndex| may be accessed (get, set, delete) directly through its + * contiguous vector of elements without fear of getters, setters, etc. along + * the prototype chain. + */ +static inline bool +CanOptimizeForDenseStorage(JSObject *arr, uint32 startingIndex, uint32 count, JSContext *cx) +{ + JS_ASSERT(UINT32_MAX - startingIndex >= count); + + uint32 length = startingIndex + count; + return arr->isDenseArray() && + !arr->getType(cx)->hasAllFlags(OBJECT_FLAG_NON_PACKED_ARRAY) && + !js_PrototypeHasIndexedProperties(cx, arr) && + length <= arr->getDenseArrayInitializedLength(); +} + +static inline bool +CopyArrayElement(JSContext *cx, JSObject *source, uint32 sourceIndex, + JSObject *target, uint32 targetIndex) +{ + if (!JS_CHECK_OPERATION_LIMIT(cx)) + return false; + + JSBool hole; + Value fromValue; + return GetElement(cx, source, sourceIndex, &hole, &fromValue) && + SetOrDeleteArrayElement(cx, target, targetIndex, hole, fromValue); +} + +/* ES5 15.4.4.12. */ static JSBool array_splice(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + + /* Step 1. */ + JSObject *obj = ToObject(cx, &args.thisv()); if (!obj) return false; - jsuint length, begin, end, count, delta, last; - JSBool hole; + /* Steps 3-4. */ + uint32 len; + if (!js_GetLengthProperty(cx, obj, &len)) + return false; - /* Create a new array value to return. */ - JSObject *obj2 = NewDenseEmptyArray(cx); - if (!obj2) - return JS_FALSE; - TryReuseArrayType(obj, obj2); - vp->setObject(*obj2); + /* Step 5. */ + double relativeStart; + if (!ToInteger(cx, argc >= 1 ? args[0] : UndefinedValue(), &relativeStart)) + return false; - /* Nothing to do if no args. Otherwise get length. */ - if (argc == 0) - return JS_TRUE; - Value *argv = JS_ARGV(cx, vp); - if (!js_GetLengthProperty(cx, obj, &length)) - return JS_FALSE; - jsuint origlength = length; + /* Step 6. */ + uint32 actualStart; + if (relativeStart < 0) + actualStart = JS_MAX(len + relativeStart, 0); + else + actualStart = JS_MIN(relativeStart, len); - /* Convert the first argument into a starting index. */ - jsdouble d; - if (!ToInteger(cx, *argv, &d)) - return JS_FALSE; - if (d < 0) { - d += length; - if (d < 0) - d = 0; - } else if (d > length) { - d = length; - } - begin = (jsuint)d; /* d has been clamped to uint32 */ - argc--; - argv++; - - /* Convert the second argument from a count into a fencepost index. */ - delta = length - begin; - if (argc == 0) { - count = delta; - end = length; - } else { - if (!ToInteger(cx, *argv, &d)) + /* Step 7. */ + uint32 actualDeleteCount; + if (argc != 1) { + jsdouble deleteCountDouble; + if (!ToInteger(cx, argc >= 2 ? args[1] : Int32Value(0), &deleteCountDouble)) return false; - if (d < 0) - d = 0; - else if (d > delta) - d = delta; - count = (jsuint)d; - end = begin + count; - argc--; - argv++; + actualDeleteCount = JS_MIN(JS_MAX(deleteCountDouble, 0), len - actualStart); + } else { + /* + * Non-standard: if start was specified but deleteCount was omitted, + * delete to the end of the array. See bug 668024 for discussion. + */ + actualDeleteCount = len - actualStart; } - AutoValueRooter tvr(cx); + JS_ASSERT(len - actualStart >= actualDeleteCount); - /* If there are elements to remove, put them into the return value. */ - if (count > 0) { - if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) && - end <= obj->getDenseArrayInitializedLength()) { - if (!InitArrayObject(cx, obj2, count, obj->getDenseArrayElements() + begin)) - return JS_FALSE; - } else { - for (last = begin; last < end; last++) { - if (!JS_CHECK_OPERATION_LIMIT(cx) || - !GetElement(cx, obj, last, &hole, tvr.addr())) { - return JS_FALSE; - } + /* Steps 2, 8-9. */ + JSObject *arr; + if (CanOptimizeForDenseStorage(obj, actualStart, actualDeleteCount, cx)) { + arr = NewDenseCopiedArray(cx, actualDeleteCount, + obj->getDenseArrayElements() + actualStart); + if (!arr) + return false; + TryReuseArrayType(obj, arr); + } else { + arr = NewDenseAllocatedArray(cx, actualDeleteCount); + if (!arr) + return false; + TryReuseArrayType(obj, arr); - /* Copy tvr.value() to the new array unless it's a hole. */ - if (!hole && !SetArrayElement(cx, obj2, last - begin, tvr.value())) - return JS_FALSE; + for (uint32 k = 0; k < actualDeleteCount; k++) { + JSBool hole; + Value fromValue; + if (!JS_CHECK_OPERATION_LIMIT(cx) || + !GetElement(cx, obj, actualStart + k, &hole, &fromValue) || + (!hole && !arr->defineElement(cx, k, fromValue))) + { + return false; } - - if (!js_SetLengthProperty(cx, obj2, count)) - return JS_FALSE; } } - /* Find the direction (up or down) to copy and make way for argv. */ - if (argc > count) { - delta = (jsuint)argc - count; - last = length; - bool optimized = false; - do { - if (!obj->isDenseArray()) - break; - if (js_PrototypeHasIndexedProperties(cx, obj)) - break; - if (length > obj->getDenseArrayInitializedLength()) - break; - if (length != 0 && obj->getDenseArrayElement(length - 1).isMagic(JS_ARRAY_HOLE)) - break; - JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, length, delta); - if (result != JSObject::ED_OK) { - if (result == JSObject::ED_FAILED) + /* Step 11. */ + uint32 itemCount = (argc >= 2) ? (argc - 2) : 0; + + if (itemCount < actualDeleteCount) { + /* Step 12: the array is being shrunk. */ + uint32 sourceIndex = actualStart + actualDeleteCount; + uint32 targetIndex = actualStart + itemCount; + uint32 finalLength = len - actualDeleteCount + itemCount; + + if (CanOptimizeForDenseStorage(obj, 0, len, cx)) { + /* Steps 12(a)-(b). */ + obj->moveDenseArrayElements(targetIndex, sourceIndex, len - sourceIndex); + + /* Steps 12(c)-(d). */ + obj->shrinkDenseArrayElements(cx, finalLength); + + /* + * The array's initialized length is now out of sync with the array + * elements: resynchronize it. + */ + if (cx->typeInferenceEnabled()) + obj->setDenseArrayInitializedLength(finalLength); + + /* Fix running enumerators for the deleted items. */ + if (!js_SuppressDeletedElements(cx, obj, finalLength, len)) + return false; + } else { + /* + * This is all very slow if the length is very large. We don't yet + * have the ability to iterate in sorted order, so we just do the + * pessimistic thing and let JS_CHECK_OPERATION_LIMIT handle the + * fallout. + */ + + /* Steps 12(a)-(b). */ + for (uint32 from = sourceIndex, to = targetIndex; from < len; from++, to++) { + JSBool hole; + Value fromValue; + if (!JS_CHECK_OPERATION_LIMIT(cx) || + !GetElement(cx, obj, from, &hole, &fromValue) || + !SetOrDeleteArrayElement(cx, obj, to, hole, fromValue)) + { return false; - JS_ASSERT(result == JSObject::ED_SPARSE); - break; - } - obj->moveDenseArrayElements(end + delta, end, last - end); - - obj->setArrayLength(cx, obj->getArrayLength() + delta); - optimized = true; - } while (false); - - if (!optimized) { - /* (uint) end could be 0, so we can't use a vanilla >= test. */ - while (last-- > end) { - if (!JS_CHECK_OPERATION_LIMIT(cx) || - !GetElement(cx, obj, last, &hole, tvr.addr()) || - !SetOrDeleteArrayElement(cx, obj, last + delta, hole, tvr.value())) { - return JS_FALSE; } } - } - length += delta; - } else if (argc < count) { - delta = count - (jsuint)argc; - if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) && - length <= obj->getDenseArrayInitializedLength()) { - obj->moveDenseArrayElements(end - delta, end, length - end); + /* Steps 12(c)-(d). */ + for (uint32 k = len; k > finalLength; k--) { + if (DeleteArrayElement(cx, obj, k - 1, true) < 0) + return false; + } + } + } else if (itemCount > actualDeleteCount) { + /* Step 13. */ + + /* + * Optimize only if the array is already dense and we can extend it to + * its new length. + */ + if (obj->isDenseArray()) { + JSObject::EnsureDenseResult res = + obj->ensureDenseArrayElements(cx, obj->getArrayLength(), + itemCount - actualDeleteCount); + if (res == JSObject::ED_FAILED) + return false; + + if (res == JSObject::ED_SPARSE) { + if (!obj->makeDenseArraySlow(cx)) + return false; + } else { + JS_ASSERT(res == JSObject::ED_OK); + } + } + + if (CanOptimizeForDenseStorage(obj, len, itemCount - actualDeleteCount, cx)) { + obj->moveDenseArrayElements(actualStart + itemCount, + actualStart + actualDeleteCount, + len - (actualStart + actualDeleteCount)); + + if (cx->typeInferenceEnabled()) + obj->setDenseArrayInitializedLength(len + itemCount - actualDeleteCount); } else { - for (last = end; last < length; last++) { + for (jsdouble k = len - actualDeleteCount; k > actualStart; k--) { + jsdouble from = k + actualDeleteCount - 1; + jsdouble to = k + itemCount - 1; + + JSBool hole; + Value fromValue; if (!JS_CHECK_OPERATION_LIMIT(cx) || - !GetElement(cx, obj, last, &hole, tvr.addr()) || - !SetOrDeleteArrayElement(cx, obj, last - delta, hole, tvr.value())) { - return JS_FALSE; + !GetElement(cx, obj, from, &hole, &fromValue) || + !SetOrDeleteArrayElement(cx, obj, to, hole, fromValue)) + { + return false; } } } - length -= delta; } - if (length < origlength && !js_SuppressDeletedIndexProperties(cx, obj, length, origlength)) - return JS_FALSE; + /* Step 10. */ + Value *items = args.array() + 2; - /* - * Copy from argv into the hole to complete the splice, and update length in - * case we deleted elements from the end. - */ - return InitArrayElements(cx, obj, begin, argc, argv, true) && - js_SetLengthProperty(cx, obj, length); + /* Steps 14-15. */ + for (uint32 k = actualStart, i = 0; i < itemCount; i++, k++) { + if (!SetArrayElement(cx, obj, k, items[i])) + return false; + } + + /* Step 16. */ + jsdouble finalLength = jsdouble(len) - actualDeleteCount + itemCount; + if (!js_SetLengthProperty(cx, obj, finalLength)) + return false; + + /* Step 17. */ + args.rval().setObject(*arr); + return true; } /* @@ -3495,8 +3571,15 @@ NewArray(JSContext *cx, jsuint length, JSObject *proto) obj->backfillDenseArrayHoles(cx); } - if (allocateCapacity && !obj->ensureSlots(cx, length)) - return NULL; + if (allocateCapacity) { + /* If ensureSlots creates dynamically allocated slots, then having fixedSlots is a waste. */ + DebugOnly oldSlots = obj->numSlots(); + + if (!obj->ensureSlots(cx, length)) + return NULL; + + JS_ASSERT_IF(obj->numFixedSlots(), oldSlots == obj->numSlots()); + } return obj; } @@ -3540,7 +3623,7 @@ mjit::stubs::NewDenseUnallocatedArray(VMFrame &f, uint32 length) #endif JSObject * -NewDenseCopiedArray(JSContext *cx, uintN length, const Value *vp, JSObject *proto) +NewDenseCopiedArray(JSContext *cx, uint32 length, const Value *vp, JSObject *proto /* = NULL */) { JSObject* obj = NewArray(cx, length, proto); if (!obj) diff --git a/js/src/jsarray.h b/js/src/jsarray.h index 3ff84c51833e..e89d811e64f8 100644 --- a/js/src/jsarray.h +++ b/js/src/jsarray.h @@ -156,7 +156,7 @@ NewDenseUnallocatedArray(JSContext *cx, uint length, JSObject *proto=NULL); /* Create a dense array with a copy of vp. */ extern JSObject * -NewDenseCopiedArray(JSContext *cx, uint length, const Value *vp, JSObject *proto=NULL); +NewDenseCopiedArray(JSContext *cx, uint32 length, const Value *vp, JSObject *proto = NULL); /* Create a sparse array. */ extern JSObject * @@ -180,6 +180,8 @@ extern JSBool array_deleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict); /* + * Copy 'length' elements from aobj to vp. + * * This function assumes 'length' is effectively the result of calling * js_GetLengthProperty on aobj. */ diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 5ec00ea30130..4f6980a3c92e 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -476,10 +476,10 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) ) { JS_ASSERT(!rt->gcRunning); - JS_UNLOCK_GC(rt); #ifdef JS_THREADSAFE - rt->gcHelperThread.waitBackgroundSweepEnd(rt); + rt->gcHelperThread.waitBackgroundSweepEnd(); #endif + JS_UNLOCK_GC(rt); if (last) { #ifdef JS_THREADSAFE @@ -559,10 +559,10 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) js_ClearContextThread(cx); JS_ASSERT_IF(JS_CLIST_IS_EMPTY(&t->contextList), !t->data.requestDepth); #endif - JS_UNLOCK_GC(rt); #ifdef JS_THREADSAFE - rt->gcHelperThread.waitBackgroundSweepEnd(rt); + rt->gcHelperThread.waitBackgroundSweepEnd(); #endif + JS_UNLOCK_GC(rt); Foreground::delete_(cx); } @@ -1177,7 +1177,10 @@ js_InvokeOperationCallback(JSContext *cx) * We have to wait until the background thread is done in order * to get a correct answer. */ - rt->gcHelperThread.waitBackgroundSweepEnd(rt); + { + AutoLockGC lock(rt); + rt->gcHelperThread.waitBackgroundSweepEnd(); + } if (checkOutOfMemory(rt)) { js_ReportOutOfMemory(cx); return false; @@ -1518,8 +1521,17 @@ JSRuntime::onTooMuchMalloc() JS_FRIEND_API(void *) JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx) { + /* + * Retry when we are done with the background sweeping and have stopped + * all the allocations and released the empty GC chunks. + */ + { #ifdef JS_THREADSAFE - gcHelperThread.waitBackgroundSweepEnd(this); + AutoLockGC lock(this); + gcHelperThread.waitBackgroundSweepOrAllocEnd(); +#endif + gcChunkPool.expire(this, true); + } if (!p) p = OffTheBooks::malloc_(nbytes); else if (p == reinterpret_cast(1)) @@ -1528,7 +1540,6 @@ JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx) p = OffTheBooks::realloc_(p, nbytes); if (p) return p; -#endif if (cx) js_ReportOutOfMemory(cx); return NULL; diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 0d1c301dbc1a..c66d2183b647 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -411,14 +411,7 @@ struct JSRuntime { */ js::gc::Chunk *gcSystemAvailableChunkListHead; js::gc::Chunk *gcUserAvailableChunkListHead; - - /* - * Singly-linked list of empty chunks and its length. We use the list not - * to release empty chunks immediately so they can be used for future - * allocations. This avoids very high overhead of chunk release/allocation. - */ - js::gc::Chunk *gcEmptyChunkListHead; - size_t gcEmptyChunkCount; + js::gc::ChunkPool gcChunkPool; js::RootedValueMap gcRootsHash; js::GCLocks gcLocksHash; diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index a151b2ea8678..d1f531e16c16 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -924,11 +924,8 @@ JSCompartment::markTrapClosuresIteratively(JSTracer *trc) for (BreakpointSiteMap::Range r = breakpointSites.all(); !r.empty(); r.popFront()) { BreakpointSite *site = r.front().value; - // Mark jsdbgapi state if any. But if we know the scriptObject, put off - // marking trap state until we know the scriptObject is live. - if (site->trapHandler && - (!site->scriptObject || !IsAboutToBeFinalized(cx, site->scriptObject))) - { + // Put off marking trap state until we know the script is live. + if (site->trapHandler && !IsAboutToBeFinalized(cx, site->script)) { if (site->trapClosure.isMarkable() && IsAboutToBeFinalized(cx, site->trapClosure.toGCThing())) { @@ -945,21 +942,19 @@ JSCompartment::sweepBreakpoints(JSContext *cx) { for (BreakpointSiteMap::Enum e(breakpointSites); !e.empty(); e.popFront()) { BreakpointSite *site = e.front().value; - if (site->scriptObject) { - // clearTrap and nextbp are necessary here to avoid possibly - // reading *site or *bp after destroying it. - bool scriptGone = IsAboutToBeFinalized(cx, site->scriptObject); - bool clearTrap = scriptGone && site->hasTrap(); - - Breakpoint *nextbp; - for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = nextbp) { - nextbp = bp->nextInSite(); - if (scriptGone || IsAboutToBeFinalized(cx, bp->debugger->toJSObject())) - bp->destroy(cx, &e); - } - - if (clearTrap) - site->clearTrap(cx, &e); + // clearTrap and nextbp are necessary here to avoid possibly + // reading *site or *bp after destroying it. + bool scriptGone = IsAboutToBeFinalized(cx, site->script); + bool clearTrap = scriptGone && site->hasTrap(); + + Breakpoint *nextbp; + for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = nextbp) { + nextbp = bp->nextInSite(); + if (scriptGone || IsAboutToBeFinalized(cx, bp->debugger->toJSObject())) + bp->destroy(cx, &e); } + + if (clearTrap) + site->clearTrap(cx, &e); } } diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index afb6006d2073..9c49c79cc86d 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -2436,7 +2436,7 @@ js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent, return NULL; js_CallNewScriptHook(cx, cfun->script(), cfun); - Debugger::onNewScript(cx, cfun->script(), cfun, Debugger::NewHeldScript); + Debugger::onNewScript(cx, cfun->script(), cfun, NULL); } } return clone; diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index a56293a43643..32fcf489b541 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -414,8 +414,138 @@ FinalizeArenas(JSContext *cx, ArenaLists::ArenaList *al, AllocKind thingKind) } } -} /* namespace gc */ -} /* namespace js */ +#ifdef JS_THREADSAFE +inline bool +ChunkPool::wantBackgroundAllocation(JSRuntime *rt) const +{ + /* + * To minimize memory waste we do not want to run the background chunk + * allocation if we have empty chunks or when the runtime needs just few + * of them. + */ + return rt->gcHelperThread.canBackgroundAllocate() && + emptyCount == 0 && + rt->gcChunkSet.count() >= 4; +} +#endif + +/* Must be called with the GC lock taken. */ +inline Chunk * +ChunkPool::get(JSRuntime *rt) +{ + JS_ASSERT(this == &rt->gcChunkPool); + + Chunk *chunk = emptyChunkListHead; + if (chunk) { + JS_ASSERT(emptyCount); + emptyChunkListHead = chunk->info.next; + --emptyCount; + } else { + JS_ASSERT(!emptyCount); + chunk = Chunk::allocate(); + if (!chunk) + return NULL; + } + JS_ASSERT(chunk->unused()); + JS_ASSERT(!rt->gcChunkSet.has(chunk)); + +#ifdef JS_THREADSAFE + if (wantBackgroundAllocation(rt)) + rt->gcHelperThread.startBackgroundAllocationIfIdle(); +#endif + + return chunk; +} + +/* Must be called either during the GC or with the GC lock taken. */ +inline void +ChunkPool::put(JSRuntime *rt, Chunk *chunk) +{ + JS_ASSERT(this == &rt->gcChunkPool); + + size_t initialAge = 0; +#ifdef JS_THREADSAFE + /* + * When we have not yet started the background finalization, we must keep + * empty chunks until we are done with all the sweeping and finalization + * that cannot be done in the background even if shouldShrink() is true. + * This way we can safely call IsAboutToBeFinalized and Cell::isMarked for + * finalized GC things in empty chunks. So we only release the chunk if we + * are called from the background thread. + */ + if (rt->gcHelperThread.sweeping()) { + if (rt->gcHelperThread.shouldShrink()) { + Chunk::release(chunk); + return; + } + + /* + * Set the age to one as we expire chunks early during the background + * sweep so this chunk already survived one GC cycle. + */ + initialAge = 1; + } +#endif + + chunk->info.age = initialAge; + chunk->info.next = emptyChunkListHead; + emptyChunkListHead = chunk; + emptyCount++; +} + +/* Must be called either during the GC or with the GC lock taken. */ +void +ChunkPool::expire(JSRuntime *rt, bool releaseAll) +{ + JS_ASSERT(this == &rt->gcChunkPool); + + /* + * Return old empty chunks to the system while preserving the order of + * other chunks in the list. This way, if the GC runs several times + * without emptying the list, the older chunks will stay at the tail + * and are more likely to reach the max age. + */ + for (Chunk **chunkp = &emptyChunkListHead; *chunkp; ) { + JS_ASSERT(emptyCount); + Chunk *chunk = *chunkp; + JS_ASSERT(chunk->unused()); + JS_ASSERT(!rt->gcChunkSet.has(chunk)); + JS_ASSERT(chunk->info.age <= MAX_EMPTY_CHUNK_AGE); + if (releaseAll || chunk->info.age == MAX_EMPTY_CHUNK_AGE) { + *chunkp = chunk->info.next; + --emptyCount; + Chunk::release(chunk); + } else { + /* Keep the chunk but increase its age. */ + ++chunk->info.age; + chunkp = &chunk->info.next; + } + } + JS_ASSERT_IF(releaseAll, !emptyCount); +} + +/* static */ Chunk * +Chunk::allocate() +{ + Chunk *chunk = static_cast(AllocGCChunk()); + if (!chunk) + return NULL; + chunk->init(); +#ifdef MOZ_GCTIMER + JS_ATOMIC_INCREMENT(&newChunkCount); +#endif + return chunk; +} + +/* static */ inline void +Chunk::release(Chunk *chunk) +{ + JS_ASSERT(chunk); +#ifdef MOZ_GCTIMER + JS_ATOMIC_INCREMENT(&destroyChunkCount); +#endif + FreeGCChunk(chunk); +} void Chunk::init() @@ -511,7 +641,7 @@ Chunk::releaseArena(ArenaHeader *aheader) JSRuntime *rt = comp->rt; #ifdef JS_THREADSAFE AutoLockGC maybeLock; - if (rt->gcHelperThread.sweeping) + if (rt->gcHelperThread.sweeping()) maybeLock.lock(rt); #endif @@ -519,7 +649,7 @@ Chunk::releaseArena(ArenaHeader *aheader) JS_ASSERT(size_t(rt->gcBytes) >= ArenaSize); JS_ASSERT(size_t(comp->gcBytes) >= ArenaSize); #ifdef JS_THREADSAFE - if (rt->gcHelperThread.sweeping) { + if (rt->gcHelperThread.sweeping()) { rt->reduceGCTriggerBytes(GC_HEAP_GROWTH_FACTOR * ArenaSize); comp->reduceGCTriggerBytes(GC_HEAP_GROWTH_FACTOR * ArenaSize); } @@ -540,40 +670,12 @@ Chunk::releaseArena(ArenaHeader *aheader) } else { rt->gcChunkSet.remove(this); removeFromAvailableList(); - - /* - * We keep empty chunks until we are done with finalization to allow - * calling IsAboutToBeFinalized/Cell::isMarked for finalized GC things - * in empty chunks. So we add the chunk to the empty set even during - * GC_SHRINK. - */ - info.age = 0; - info.next = rt->gcEmptyChunkListHead; - rt->gcEmptyChunkListHead = this; - rt->gcEmptyChunkCount++; + rt->gcChunkPool.put(rt, this); } } -inline Chunk * -AllocateGCChunk(JSRuntime *rt) -{ - Chunk *p = static_cast(AllocGCChunk()); -#ifdef MOZ_GCTIMER - if (p) - JS_ATOMIC_INCREMENT(&newChunkCount); -#endif - return p; -} - -inline void -ReleaseGCChunk(JSRuntime *rt, Chunk *p) -{ - JS_ASSERT(p); -#ifdef MOZ_GCTIMER - JS_ATOMIC_INCREMENT(&destroyChunkCount); -#endif - FreeGCChunk(p); -} +} /* namespace gc */ +} /* namespace js */ /* The caller must hold the GC lock. */ static Chunk * @@ -585,25 +687,11 @@ PickChunk(JSCompartment *comp) if (chunk) return chunk; - /* - * We do not have available chunks, either get one from the empty set or - * allocate one. - */ - chunk = rt->gcEmptyChunkListHead; - if (chunk) { - JS_ASSERT(chunk->unused()); - JS_ASSERT(!rt->gcChunkSet.has(chunk)); - JS_ASSERT(rt->gcEmptyChunkCount >= 1); - rt->gcEmptyChunkListHead = chunk->info.next; - rt->gcEmptyChunkCount--; - } else { - chunk = AllocateGCChunk(rt); - if (!chunk) - return NULL; + chunk = rt->gcChunkPool.get(rt); + if (!chunk) + return NULL; - chunk->init(); - rt->gcChunkAllocationSinceLastGC = true; - } + rt->gcChunkAllocationSinceLastGC = true; /* * FIXME bug 583732 - chunk is newly allocated and cannot be present in @@ -612,7 +700,7 @@ PickChunk(JSCompartment *comp) GCChunkSet::AddPtr p = rt->gcChunkSet.lookupForAdd(chunk); JS_ASSERT(!p); if (!rt->gcChunkSet.add(p, chunk)) { - ReleaseGCChunk(rt, chunk); + Chunk::release(chunk); return NULL; } @@ -623,30 +711,6 @@ PickChunk(JSCompartment *comp) return chunk; } -static void -ExpireGCChunks(JSRuntime *rt, JSGCInvocationKind gckind) -{ - AutoLockGC lock(rt); - - /* Return old empty chunks to the system. */ - for (Chunk **chunkp = &rt->gcEmptyChunkListHead; *chunkp; ) { - JS_ASSERT(rt->gcEmptyChunkCount); - Chunk *chunk = *chunkp; - JS_ASSERT(chunk->unused()); - JS_ASSERT(!rt->gcChunkSet.has(chunk)); - JS_ASSERT(chunk->info.age <= MAX_EMPTY_CHUNK_AGE); - if (gckind == GC_SHRINK || chunk->info.age == MAX_EMPTY_CHUNK_AGE) { - *chunkp = chunk->info.next; - --rt->gcEmptyChunkCount; - ReleaseGCChunk(rt, chunk); - } else { - /* Keep the chunk but increase its age. */ - ++chunk->info.age; - chunkp = &chunk->info.next; - } - } -} - JS_FRIEND_API(bool) IsAboutToBeFinalized(JSContext *cx, const void *thing) { @@ -698,7 +762,7 @@ js_InitGC(JSRuntime *rt, uint32 maxbytes) rt->requestDone = JS_NEW_CONDVAR(rt->gcLock); if (!rt->requestDone) return false; - if (!rt->gcHelperThread.init(rt)) + if (!rt->gcHelperThread.init()) return false; #endif @@ -995,20 +1059,19 @@ js_FinishGC(JSRuntime *rt) rt->gcSystemAvailableChunkListHead = NULL; rt->gcUserAvailableChunkListHead = NULL; for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront()) - ReleaseGCChunk(rt, r.front()); + Chunk::release(r.front()); rt->gcChunkSet.clear(); - for (Chunk *chunk = rt->gcEmptyChunkListHead; chunk; ) { - Chunk *next = chunk->info.next; - ReleaseGCChunk(rt, chunk); - chunk = next; - } - rt->gcEmptyChunkListHead = NULL; - rt->gcEmptyChunkCount = 0; #ifdef JS_THREADSAFE - rt->gcHelperThread.finish(rt); + rt->gcHelperThread.finish(); #endif + /* + * Finish the pool after the background thread stops in case it was doing + * the background sweeping. + */ + rt->gcChunkPool.expire(rt, true); + #ifdef DEBUG if (!rt->gcRootsHash.empty()) CheckLeakedRoots(rt); @@ -1237,7 +1300,7 @@ ArenaLists::allocateFromArena(JSCompartment *comp, AllocKind thingKind) * added new empty arenas. */ JS_ASSERT(*bfs == BFS_RUN); - comp->rt->gcHelperThread.waitBackgroundSweepEnd(comp->rt, false); + comp->rt->gcHelperThread.waitBackgroundSweepEnd(); JS_ASSERT(*bfs == BFS_JUST_FINISHED || *bfs == BFS_DONE); } } @@ -1320,7 +1383,7 @@ ArenaLists::finalizeLater(JSContext *cx, AllocKind thingKind) thingKind == FINALIZE_STRING); #ifdef JS_THREADSAFE - JS_ASSERT(!cx->runtime->gcHelperThread.sweeping); + JS_ASSERT(!cx->runtime->gcHelperThread.sweeping()); ArenaList *al = &arenaLists[thingKind]; if (!al->head) { @@ -1468,8 +1531,10 @@ RunLastDitchGC(JSContext *cx) js_GC(cx, rt->gcTriggerCompartment, GC_NORMAL); #ifdef JS_THREADSAFE - if (rt->gcBytes >= rt->gcMaxBytes) - cx->runtime->gcHelperThread.waitBackgroundSweepEnd(cx->runtime); + if (rt->gcBytes >= rt->gcMaxBytes) { + AutoLockGC lock(rt); + cx->runtime->gcHelperThread.waitBackgroundSweepEnd(); + } #endif } @@ -1999,7 +2064,7 @@ MaybeGC(JSContext *cx) */ int64 now = PRMJ_Now(); if (rt->gcNextFullGCTime && rt->gcNextFullGCTime <= now) { - if (rt->gcChunkAllocationSinceLastGC || rt->gcEmptyChunkListHead) { + if (rt->gcChunkAllocationSinceLastGC) { GCREASON(MAYBEGC); js_GC(cx, NULL, GC_SHRINK); } else { @@ -2015,28 +2080,34 @@ MaybeGC(JSContext *cx) namespace js { bool -GCHelperThread::init(JSRuntime *rt) +GCHelperThread::init() { if (!(wakeup = PR_NewCondVar(rt->gcLock))) return false; - if (!(sweepingDone = PR_NewCondVar(rt->gcLock))) + if (!(done = PR_NewCondVar(rt->gcLock))) return false; - thread = PR_CreateThread(PR_USER_THREAD, threadMain, rt, PR_PRIORITY_NORMAL, + thread = PR_CreateThread(PR_USER_THREAD, threadMain, this, PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); - return !!thread; + if (!thread) + return false; + backgroundAllocation = (js_GetCPUCount() >= 2); + return true; } void -GCHelperThread::finish(JSRuntime *rt) +GCHelperThread::finish() { PRThread *join = NULL; { AutoLockGC lock(rt); - if (thread && !shutdown) { - shutdown = true; - PR_NotifyCondVar(wakeup); + if (thread && state != SHUTDOWN) { + /* The allocation should have been stopped during the last GC. */ + JS_ASSERT(state == IDLE || state == SWEEPING); + if (state == IDLE) + PR_NotifyCondVar(wakeup); + state = SHUTDOWN; join = thread; } } @@ -2046,66 +2117,113 @@ GCHelperThread::finish(JSRuntime *rt) } if (wakeup) PR_DestroyCondVar(wakeup); - if (sweepingDone) - PR_DestroyCondVar(sweepingDone); + if (done) + PR_DestroyCondVar(done); } /* static */ void GCHelperThread::threadMain(void *arg) { - JSRuntime *rt = static_cast(arg); - rt->gcHelperThread.threadLoop(rt); + static_cast(arg)->threadLoop(); } void -GCHelperThread::threadLoop(JSRuntime *rt) +GCHelperThread::threadLoop() { AutoLockGC lock(rt); - while (!shutdown) { - /* - * Sweeping can be true here on the first iteration if a GC and the - * corresponding startBackgroundSweep call happen before this thread - * has a chance to run. - */ - if (!sweeping) + + /* + * Even on the first iteration the state can be SHUTDOWN or SWEEPING if + * the stop request or the GC and the corresponding startBackgroundSweep call + * happen before this thread has a chance to run. + */ + for (;;) { + switch (state) { + case SHUTDOWN: + return; + case IDLE: PR_WaitCondVar(wakeup, PR_INTERVAL_NO_TIMEOUT); - if (sweeping) { - AutoUnlockGC unlock(rt); + break; + case SWEEPING: doSweep(); + if (state == SWEEPING) + state = IDLE; + PR_NotifyAllCondVar(done); + break; + case ALLOCATING: + do { + Chunk *chunk; + { + AutoUnlockGC unlock(rt); + chunk = Chunk::allocate(); + } + + /* OOM stops the background allocation. */ + if (!chunk) + break; + rt->gcChunkPool.put(rt, chunk); + } while (state == ALLOCATING && rt->gcChunkPool.wantBackgroundAllocation(rt)); + if (state == ALLOCATING) + state = IDLE; + break; + case CANCEL_ALLOCATION: + state = IDLE; + PR_NotifyAllCondVar(done); + break; } - sweeping = false; - PR_NotifyAllCondVar(sweepingDone); } } bool -GCHelperThread::prepareForBackgroundSweep(JSContext *context) { - size_t maxArenaLists = MAX_BACKGROUND_FINALIZE_KINDS * context->runtime->compartments.length(); +GCHelperThread::prepareForBackgroundSweep(JSContext *cx) +{ + JS_ASSERT(cx->runtime == rt); + JS_ASSERT(state == IDLE); + size_t maxArenaLists = MAX_BACKGROUND_FINALIZE_KINDS * rt->compartments.length(); if (!finalizeVector.reserve(maxArenaLists)) return false; - cx = context; + context = cx; return true; } -void -GCHelperThread::startBackgroundSweep(JSRuntime *rt, JSGCInvocationKind gckind) +/* Must be called with the GC lock taken. */ +inline void +GCHelperThread::startBackgroundSweep(bool shouldShrink) { /* The caller takes the GC lock. */ - JS_ASSERT(!sweeping); - lastGCKind = gckind; - sweeping = true; + JS_ASSERT(state == IDLE); + shrinkFlag = shouldShrink; + state = SWEEPING; PR_NotifyCondVar(wakeup); } +/* Must be called with the GC lock taken. */ void -GCHelperThread::waitBackgroundSweepEnd(JSRuntime *rt, bool gcUnlocked) +GCHelperThread::waitBackgroundSweepEnd() { - AutoLockGC maybeLock; - if (gcUnlocked) - maybeLock.lock(rt); - while (sweeping) - PR_WaitCondVar(sweepingDone, PR_INTERVAL_NO_TIMEOUT); + while (state == SWEEPING) + PR_WaitCondVar(done, PR_INTERVAL_NO_TIMEOUT); +} + +/* Must be called with the GC lock taken. */ +void +GCHelperThread::waitBackgroundSweepOrAllocEnd() +{ + if (state == ALLOCATING) + state = CANCEL_ALLOCATION; + while (state == SWEEPING || state == CANCEL_ALLOCATION) + PR_WaitCondVar(done, PR_INTERVAL_NO_TIMEOUT); +} + +/* Must be called with the GC lock taken. */ +inline void +GCHelperThread::startBackgroundAllocationIfIdle() +{ + if (state == IDLE) { + state = ALLOCATING; + PR_NotifyCondVar(wakeup); + } } JS_FRIEND_API(void) @@ -2127,20 +2245,29 @@ GCHelperThread::replenishAndFreeLater(void *ptr) Foreground::free_(ptr); } +/* Must be called with the GC lock taken. */ void GCHelperThread::doSweep() { - JS_ASSERT(cx); + JS_ASSERT(context); + + /* + * Expire the chunks released during the GC so they will be available to + * the rest of the system immediately. + */ + rt->gcChunkPool.expire(rt, shouldShrink()); + + AutoUnlockGC unlock(rt); /* * We must finalize in the insert order, see comments in * finalizeObjects. */ for (ArenaHeader **i = finalizeVector.begin(); i != finalizeVector.end(); ++i) - ArenaLists::backgroundFinalize(cx, *i); + ArenaLists::backgroundFinalize(context, *i); finalizeVector.resize(0); - ExpireGCChunks(cx->runtime, lastGCKind); - cx = NULL; + + context = NULL; if (freeCursor) { void **array = freeCursorEnd - FREE_ARRAY_LENGTH; @@ -2384,7 +2511,7 @@ SweepPhase(JSContext *cx, GCMarker *gcmarker, JSGCInvocationKind gckind GCTIMER_ * use IsAboutToBeFinalized(). * This is done on the GCHelperThread if JS_THREADSAFE is defined. */ - ExpireGCChunks(rt, gckind); + rt->gcChunkPool.expire(rt, gckind == GC_SHRINK); #endif GCTIMESTAMP(sweepDestroyEnd); @@ -2397,8 +2524,7 @@ SweepPhase(JSContext *cx, GCMarker *gcmarker, JSGCInvocationKind gckind GCTIMER_ * * In a JS_THREADSAFE build, the calling thread must be rt->gcThread and each * other thread must be either outside all requests or blocked waiting for GC - * to finish. Note that the caller does not hold rt->gcLock. - * If comp is set, we perform a single-compartment GC. + * to finish. The caller must hold rt->gcLock. */ static void MarkAndSweep(JSContext *cx, JSGCInvocationKind gckind GCTIMER_PARAM) @@ -2413,6 +2539,8 @@ MarkAndSweep(JSContext *cx, JSGCInvocationKind gckind GCTIMER_PARAM) /* Reset malloc counter. */ rt->resetGCMallocBytes(); + AutoUnlockGC unlock(rt); + GCMarker gcmarker(cx); JS_ASSERT(IS_GC_MARKING_TRACER(&gcmarker)); JS_ASSERT(gcmarker.getMarkColor() == BLACK); @@ -2649,29 +2777,29 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIMER_P rt->gcCurrentCompartment = comp; rt->gcMarkAndSweep = true; - { - AutoUnlockGC unlock(rt); #ifdef JS_THREADSAFE - /* - * As we about to purge caches and clear the mark bits we must wait - * for any background finalization to finish. - */ - JS_ASSERT(!cx->gcBackgroundFree); - rt->gcHelperThread.waitBackgroundSweepEnd(rt); - if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) { - if (rt->gcHelperThread.prepareForBackgroundSweep(cx)) - cx->gcBackgroundFree = &rt->gcHelperThread; - } -#endif - MarkAndSweep(cx, gckind GCTIMER_ARG); + /* + * As we about to purge caches and clear the mark bits we must wait for + * any background finalization to finish. We must also wait for the + * background allocation to finish so we can avoid taking the GC lock + * when manipulating the chunks during the GC. + */ + JS_ASSERT(!cx->gcBackgroundFree); + rt->gcHelperThread.waitBackgroundSweepOrAllocEnd(); + if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) { + if (rt->gcHelperThread.prepareForBackgroundSweep(cx)) + cx->gcBackgroundFree = &rt->gcHelperThread; } +#endif + + MarkAndSweep(cx, gckind GCTIMER_ARG); #ifdef JS_THREADSAFE if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) { JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread); cx->gcBackgroundFree = NULL; - rt->gcHelperThread.startBackgroundSweep(rt, gckind); + rt->gcHelperThread.startBackgroundSweep(gckind == GC_SHRINK); } else { JS_ASSERT(!cx->gcBackgroundFree); } @@ -2744,9 +2872,6 @@ js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind) } { -#ifdef JS_THREADSAFE - rt->gcHelperThread.waitBackgroundSweepEnd(rt); -#endif /* Lock out other GC allocator and collector invocations. */ AutoLockGC lock(rt); rt->gcPoke = false; @@ -2803,7 +2928,7 @@ TraceRuntime(JSTracer *trc) AutoLockGC lock(rt); AutoGCSession gcsession(cx); - rt->gcHelperThread.waitBackgroundSweepEnd(rt, false); + rt->gcHelperThread.waitBackgroundSweepEnd(); AutoUnlockGC unlock(rt); AutoCopyFreeListToArenas copy(rt); @@ -2867,7 +2992,7 @@ IterateCompartmentsArenasCells(JSContext *cx, void *data, AutoLockGC lock(rt); AutoGCSession gcsession(cx); #ifdef JS_THREADSAFE - rt->gcHelperThread.waitBackgroundSweepEnd(rt, false); + rt->gcHelperThread.waitBackgroundSweepEnd(); #endif AutoUnlockGC unlock(rt); @@ -2901,7 +3026,7 @@ IterateCells(JSContext *cx, JSCompartment *compartment, AllocKind thingKind, AutoLockGC lock(rt); AutoGCSession gcsession(cx); #ifdef JS_THREADSAFE - rt->gcHelperThread.waitBackgroundSweepEnd(rt, false); + rt->gcHelperThread.waitBackgroundSweepEnd(); #endif AutoUnlockGC unlock(rt); diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 7cd570267b2d..a9e5654b5b15 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -641,8 +641,6 @@ struct Chunk { return addr; } - void init(); - bool unused() const { return info.numFree == ArenasPerChunk; } @@ -657,11 +655,42 @@ struct Chunk { ArenaHeader *allocateArena(JSCompartment *comp, AllocKind kind); void releaseArena(ArenaHeader *aheader); + + static Chunk *allocate(); + static inline void release(Chunk *chunk); + + private: + inline void init(); }; JS_STATIC_ASSERT(sizeof(Chunk) <= GC_CHUNK_SIZE); JS_STATIC_ASSERT(sizeof(Chunk) + BytesPerArena > GC_CHUNK_SIZE); +class ChunkPool { + Chunk *emptyChunkListHead; + size_t emptyCount; + + public: + ChunkPool() + : emptyChunkListHead(NULL), + emptyCount(0) { } + + size_t getEmptyCount() const { + return emptyCount; + } + + inline bool wantBackgroundAllocation(JSRuntime *rt) const; + + /* Must be called with the GC lock taken. */ + inline Chunk *get(JSRuntime *rt); + + /* Must be called either during the GC or with the GC lock taken. */ + inline void put(JSRuntime *rt, Chunk *chunk); + + /* Must be called either during the GC or with the GC lock taken. */ + void expire(JSRuntime *rt, bool releaseAll); +}; + inline uintptr_t Cell::address() const { @@ -1288,26 +1317,36 @@ namespace js { #ifdef JS_THREADSAFE -/* - * During the finalization we do not free immediately. Rather we add the - * corresponding pointers to a buffer which we later release on a separated - * thread. - * - * The buffer is implemented as a vector of 64K arrays of pointers, not as a - * simple vector, to avoid realloc calls during the vector growth and to not - * bloat the binary size of the inlined freeLater method. Any OOM during - * buffer growth results in the pointer being freed immediately. - */ class GCHelperThread { + enum State { + IDLE, + SWEEPING, + ALLOCATING, + CANCEL_ALLOCATION, + SHUTDOWN + }; + + /* + * During the finalization we do not free immediately. Rather we add the + * corresponding pointers to a buffer which we later release on a + * separated thread. + * + * The buffer is implemented as a vector of 64K arrays of pointers, not as + * a simple vector, to avoid realloc calls during the vector growth and to + * not bloat the binary size of the inlined freeLater method. Any OOM + * during buffer growth results in the pointer being freed immediately. + */ static const size_t FREE_ARRAY_SIZE = size_t(1) << 16; static const size_t FREE_ARRAY_LENGTH = FREE_ARRAY_SIZE / sizeof(void *); - JSContext *cx; - PRThread* thread; - PRCondVar* wakeup; - PRCondVar* sweepingDone; - bool shutdown; - JSGCInvocationKind lastGCKind; + JSRuntime *const rt; + PRThread *thread; + PRCondVar *wakeup; + PRCondVar *done; + volatile State state; + + JSContext *context; + bool shrinkFlag; Vector freeVector; void **freeCursor; @@ -1315,6 +1354,8 @@ class GCHelperThread { Vector finalizeVector; + bool backgroundAllocation; + friend struct js::gc::ArenaLists; JS_FRIEND_API(void) @@ -1328,38 +1369,69 @@ class GCHelperThread { } static void threadMain(void* arg); + void threadLoop(); - void threadLoop(JSRuntime *rt); + /* Must be called with the GC lock taken. */ void doSweep(); public: - GCHelperThread() - : thread(NULL), + GCHelperThread(JSRuntime *rt) + : rt(rt), + thread(NULL), wakeup(NULL), - sweepingDone(NULL), - shutdown(false), + done(NULL), + state(IDLE), freeCursor(NULL), freeCursorEnd(NULL), - sweeping(false) { } + backgroundAllocation(true) + { } - volatile bool sweeping; - bool init(JSRuntime *rt); - void finish(JSRuntime *rt); + bool init(); + void finish(); - /* Must be called with GC lock taken. */ - void startBackgroundSweep(JSRuntime *rt, JSGCInvocationKind gckind); + /* Must be called with the GC lock taken. */ + inline void startBackgroundSweep(bool shouldShrink); - void waitBackgroundSweepEnd(JSRuntime *rt, bool gcUnlocked = true); + /* Must be called with the GC lock taken. */ + void waitBackgroundSweepEnd(); + + /* Must be called with the GC lock taken. */ + void waitBackgroundSweepOrAllocEnd(); + + /* Must be called with the GC lock taken. */ + inline void startBackgroundAllocationIfIdle(); + + bool canBackgroundAllocate() const { + return backgroundAllocation; + } + + void disableBackgroundAllocation() { + backgroundAllocation = false; + } + + /* + * Outside the GC lock may give true answer when in fact the sweeping has + * been done. + */ + bool sweeping() const { + return state == SWEEPING; + } + + bool shouldShrink() const { + JS_ASSERT(sweeping()); + return shrinkFlag; + } void freeLater(void *ptr) { - JS_ASSERT(!sweeping); + JS_ASSERT(!sweeping()); if (freeCursor != freeCursorEnd) *freeCursor++ = ptr; else replenishAndFreeLater(ptr); } - bool prepareForBackgroundSweep(JSContext *context); + /* Must be called with the GC lock taken. */ + bool prepareForBackgroundSweep(JSContext *cx); }; #endif /* JS_THREADSAFE */ diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index c6e7ad5e519a..90ed6c5d018d 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -2724,6 +2724,14 @@ TypeObject::addProperty(JSContext *cx, jsid id, Property **pprop) if (shape) UpdatePropertyType(cx, &base->types, singleton, shape, false); } + + if (singleton->watched()) { + /* + * Mark the property as configured, to inhibit optimizations on it + * and avoid bypassing the watchpoint handler. + */ + base->types.setOwnProperty(cx, true); + } } *pprop = base; @@ -4936,14 +4944,6 @@ IsAboutToBeFinalized(JSContext *cx, TypeObjectKey *key) return !reinterpret_cast((jsuword) key & ~1)->isMarked(); } -inline bool -ScriptIsAboutToBeFinalized(JSContext *cx, JSScript *script, JSFunction *fun) -{ - return script->isCachedEval || - (script->u.object && IsAboutToBeFinalized(cx, script->u.object)) || - (fun && IsAboutToBeFinalized(cx, fun)); -} - void TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, Type type) { diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index dbee3383b365..744ae0622092 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -178,6 +178,17 @@ Enumerate(JSContext *cx, JSObject *obj, JSObject *pobj, jsid id, { JS_ASSERT_IF(flags & JSITER_OWNONLY, obj == pobj); + /* + * We implement __proto__ using a property on |Object.prototype|, but + * because __proto__ is highly deserving of removal, we don't want it to + * show up in property enumeration, even if only for |Object.prototype| + * (think introspection by Prototype-like frameworks that add methods to + * the built-in prototypes). So exclude __proto__ if the object where the + * property was found has no [[Prototype]] and might be |Object.prototype|. + */ + if (JS_UNLIKELY(!pobj->getProto() && JSID_IS_ATOM(id, cx->runtime->atomState.protoAtom))) + return true; + if (!(flags & JSITER_OWNONLY) || pobj->isProxy() || pobj->getOps()->enumerate) { /* If we've already seen this, we definitely won't add it. */ IdSet::AddPtr p = ht.lookupForAdd(id); @@ -927,18 +938,31 @@ js_SuppressDeletedElement(JSContext *cx, JSObject *obj, uint32 index) } class IndexRangePredicate { - jsint begin, end; -public: - IndexRangePredicate(jsint begin, jsint end) : begin(begin), end(end) {} + uint32 begin, end; + + public: + IndexRangePredicate(uint32 begin, uint32 end) : begin(begin), end(end) {} bool operator()(jsid id) { - return JSID_IS_INT(id) && begin <= JSID_TO_INT(id) && JSID_TO_INT(id) < end; + if (JSID_IS_INT(id)) { + jsint i = JSID_TO_INT(id); + return i > 0 && begin <= uint32(i) && uint32(i) < end; + } + + if (JS_LIKELY(JSID_IS_ATOM(id))) { + JSAtom *atom = JSID_TO_ATOM(id); + uint32 index; + return atom->isIndex(&index) && begin <= index && index < end; + } + + return false; } + bool matchesAtMostOne() { return false; } }; bool -js_SuppressDeletedIndexProperties(JSContext *cx, JSObject *obj, jsint begin, jsint end) +js_SuppressDeletedElements(JSContext *cx, JSObject *obj, uint32 begin, uint32 end) { return SuppressDeletedPropertyHelper(cx, obj, IndexRangePredicate(begin, end)); } diff --git a/js/src/jsiter.h b/js/src/jsiter.h index 6a812731e924..b7396c615f18 100644 --- a/js/src/jsiter.h +++ b/js/src/jsiter.h @@ -140,7 +140,7 @@ bool js_SuppressDeletedElement(JSContext *cx, JSObject *obj, uint32 index); bool -js_SuppressDeletedIndexProperties(JSContext *cx, JSObject *obj, jsint begin, jsint end); +js_SuppressDeletedElements(JSContext *cx, JSObject *obj, uint32 begin, uint32 end); /* * IteratorMore() indicates whether another value is available. It might diff --git a/js/src/jslock.cpp b/js/src/jslock.cpp index a9bb5c2301c1..831423656f78 100644 --- a/js/src/jslock.cpp +++ b/js/src/jslock.cpp @@ -44,6 +44,13 @@ */ #include #include + +#ifdef XP_WIN +# include "jswin.h" +#else +# include +#endif + #include "jspubtd.h" #include "jsutil.h" #include "jstypes.h" @@ -73,7 +80,6 @@ extern long __cdecl _InterlockedCompareExchange(long *volatile dest, long exchange, long comp); JS_END_EXTERN_C #pragma intrinsic(_InterlockedCompareExchange) - JS_STATIC_ASSERT(sizeof(jsword) == sizeof(long)); static JS_ALWAYS_INLINE int @@ -92,11 +98,12 @@ NativeCompareAndSwap(volatile jsword *w, jsword ov, jsword nv) } #elif defined(_MSC_VER) && (defined(_M_AMD64) || defined(_M_X64)) -JS_BEGIN_EXTERN_C -extern long long __cdecl -_InterlockedCompareExchange64(long long *volatile dest, long long exchange, long long comp); -JS_END_EXTERN_C +/* + * Compared with the _InterlockedCompareExchange in the 32 bit case above MSVC + * declares _InterlockedCompareExchange64 through . + */ #pragma intrinsic(_InterlockedCompareExchange64) +JS_STATIC_ASSERT(sizeof(jsword) == sizeof(long long)); static JS_ALWAYS_INLINE int NativeCompareAndSwap(volatile jsword *w, jsword ov, jsword nv) @@ -304,6 +311,23 @@ js_AtomicClearMask(volatile jsword *w, jsword mask) } while (!js_CompareAndSwap(w, ov, nv)); } +unsigned +js_GetCPUCount() +{ + static unsigned ncpus = 0; + if (ncpus == 0) { +# ifdef XP_WIN + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + ncpus = unsigned(sysinfo.dwNumberOfProcessors); +# else + long n = sysconf(_SC_NPROCESSORS_ONLN); + ncpus = (n > 0) ? unsigned(n) : 1; +# endif + } + return ncpus; +} + #ifndef NSPR_LOCK struct JSFatLock { diff --git a/js/src/jslock.h b/js/src/jslock.h index d8246b505acc..add2651fe6f6 100644 --- a/js/src/jslock.h +++ b/js/src/jslock.h @@ -198,6 +198,9 @@ js_AtomicClearMask(volatile jsword *w, jsword mask); #define JS_ATOMIC_SET_MASK(w, mask) js_AtomicSetMask(w, mask) #define JS_ATOMIC_CLEAR_MASK(w, mask) js_AtomicClearMask(w, mask) +extern unsigned +js_GetCPUCount(); + #else static inline JSBool @@ -209,6 +212,12 @@ js_CompareAndSwap(jsword *w, jsword ov, jsword nv) #define JS_ATOMIC_SET_MASK(w, mask) (*(w) |= (mask)) #define JS_ATOMIC_CLEAR_MASK(w, mask) (*(w) &= ~(mask)) +static inline unsigned +js_GetCPUCount() +{ + return 1; +} + #endif #ifdef __cplusplus diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 582314167ca3..5eaec06f5300 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -1251,11 +1251,11 @@ JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg) /* Tell the debugger about this compiled script. */ js_CallNewScriptHook(cx, script, fun); if (!cg->parent) { - Debugger::onNewScript(cx, script, - fun ? fun : (script->u.object ? script->u.object : cg->scopeChain()), - (fun || script->u.object) - ? Debugger::NewHeldScript - : Debugger::NewNonHeldScript); + JSObject *owner = fun ? fun : script->u.object; + GlobalObject *compileAndGoGlobal = NULL; + if (script->compileAndGo) + compileAndGoGlobal = (owner ? owner : cg->scopeChain())->getGlobal(); + Debugger::onNewScript(cx, script, owner, compileAndGoGlobal); } return script; @@ -1330,7 +1330,6 @@ js_CallDestroyScriptHook(JSContext *cx, JSScript *script) if (JSDestroyScriptHook hook = cx->debugHooks->destroyScriptHook) hook(cx, script, cx->debugHooks->destroyScriptHookData); script->callDestroyHook = false; - Debugger::onDestroyScript(script); JS_ClearScriptTraps(cx, script); } diff --git a/js/src/jsweakmap.h b/js/src/jsweakmap.h index e8e155828d5c..4135236fb833 100644 --- a/js/src/jsweakmap.h +++ b/js/src/jsweakmap.h @@ -291,14 +291,14 @@ class DefaultMarkPolicy { }; template <> -class DefaultMarkPolicy { +class DefaultMarkPolicy { protected: JSTracer *tracer; public: DefaultMarkPolicy(JSTracer *t) : tracer(t) { } - bool keyMarked(JSObject *k) { return !IsAboutToBeFinalized(tracer->context, k); } + bool keyMarked(gc::Cell *k) { return !IsAboutToBeFinalized(tracer->context, k); } bool valueMarked(JSObject *v) { return !IsAboutToBeFinalized(tracer->context, v); } - bool markEntryIfLive(JSObject *k, JSObject *v) { + bool markEntryIfLive(gc::Cell *k, JSObject *v) { if (keyMarked(k) && !valueMarked(v)) { js::gc::MarkObject(tracer, *v, "WeakMap entry value"); return true; @@ -317,7 +317,7 @@ class DefaultMarkPolicy { // default mark policy. We give it a distinct name anyway, in case this ever // changes. // -typedef DefaultMarkPolicy CrossCompartmentMarkPolicy; +typedef DefaultMarkPolicy CrossCompartmentMarkPolicy; } diff --git a/js/src/jsxdrapi.cpp b/js/src/jsxdrapi.cpp index 6740c91f208a..38b434a7bad8 100644 --- a/js/src/jsxdrapi.cpp +++ b/js/src/jsxdrapi.cpp @@ -718,10 +718,11 @@ JS_XDRScript(JSXDRState *xdr, JSScript **scriptp) return false; if (xdr->mode == JSXDR_DECODE) { + JS_ASSERT(!script->compileAndGo); if (!js_NewScriptObject(xdr->cx, script)) return false; js_CallNewScriptHook(xdr->cx, script, NULL); - Debugger::onNewScript(xdr->cx, script, script->u.object, Debugger::NewHeldScript); + Debugger::onNewScript(xdr->cx, script, script->u.object, NULL); *scriptp = script; } diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 25a8ef41083d..b54a0b09217f 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -1587,6 +1587,8 @@ mjit::Compiler::generateMethod() /* All join points have synced state if we aren't doing cross-branch regalloc. */ opinfo->safePoint = true; } + } else if (opinfo->safePoint && !cx->typeInferenceEnabled()) { + frame.syncAndForgetEverything(); } frame.assertValidRegisterState(); a->jumpMap[uint32(PC - script->code)] = masm.label(); diff --git a/js/src/methodjit/Compiler.h b/js/src/methodjit/Compiler.h index 1d5e0d8b7901..dfbb1b43908a 100644 --- a/js/src/methodjit/Compiler.h +++ b/js/src/methodjit/Compiler.h @@ -723,6 +723,7 @@ class Compiler : public BaseCompiler bool isCacheableBaseAndIndex(FrameEntry *obj, FrameEntry *id); void jsop_stricteq(JSOp op); bool jsop_equality(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused); + CompileStatus jsop_equality_obj_obj(JSOp op, jsbytecode *target, JSOp fused); bool jsop_equality_int_string(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused); void jsop_pos(); @@ -739,8 +740,10 @@ class Compiler : public BaseCompiler return ifeq ? Assembler::GreaterThanOrEqual : Assembler::LessThan; case JSOP_LE: return ifeq ? Assembler::GreaterThan : Assembler::LessThanOrEqual; + case JSOP_STRICTEQ: case JSOP_EQ: return ifeq ? Assembler::NotEqual : Assembler::Equal; + case JSOP_STRICTNE: case JSOP_NE: return ifeq ? Assembler::Equal : Assembler::NotEqual; default: diff --git a/js/src/methodjit/FastOps.cpp b/js/src/methodjit/FastOps.cpp index 6bebd1a8a077..49bbb3d2770e 100644 --- a/js/src/methodjit/FastOps.cpp +++ b/js/src/methodjit/FastOps.cpp @@ -403,6 +403,58 @@ CheckNullOrUndefined(FrameEntry *fe) return type == JSVAL_TYPE_NULL || type == JSVAL_TYPE_UNDEFINED; } +CompileStatus +mjit::Compiler::jsop_equality_obj_obj(JSOp op, jsbytecode *target, JSOp fused) +{ + FrameEntry *rhs = frame.peek(-1); + FrameEntry *lhs = frame.peek(-2); + + JS_ASSERT(cx->typeInferenceEnabled() && + lhs->isType(JSVAL_TYPE_OBJECT) && rhs->isType(JSVAL_TYPE_OBJECT)); + + /* + * Handle equality between two objects. We have to ensure there is no + * special equality operator on either object, if that passes then + * this is a pointer comparison. + */ + types::TypeSet *lhsTypes = analysis->poppedTypes(PC, 1); + types::TypeSet *rhsTypes = analysis->poppedTypes(PC, 0); + if (!lhsTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPECIAL_EQUALITY) && + !rhsTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPECIAL_EQUALITY)) { + /* :TODO: Merge with jsop_relational_int? */ + JS_ASSERT_IF(!target, fused != JSOP_IFEQ); + frame.forgetMismatchedObject(lhs); + frame.forgetMismatchedObject(rhs); + Assembler::Condition cond = GetCompareCondition(op, fused); + if (target) { + Jump sj = stubcc.masm.branchTest32(GetStubCompareCondition(fused), + Registers::ReturnReg, Registers::ReturnReg); + if (!frame.syncForBranch(target, Uses(2))) + return Compile_Error; + RegisterID lreg = frame.tempRegForData(lhs); + frame.pinReg(lreg); + RegisterID rreg = frame.tempRegForData(rhs); + frame.unpinReg(lreg); + Jump fast = masm.branchPtr(cond, lreg, rreg); + frame.popn(2); + return jumpAndTrace(fast, target, &sj) ? Compile_Okay : Compile_Error; + } else { + RegisterID result = frame.allocReg(); + RegisterID lreg = frame.tempRegForData(lhs); + frame.pinReg(lreg); + RegisterID rreg = frame.tempRegForData(rhs); + frame.unpinReg(lreg); + masm.branchValue(cond, lreg, rreg, result); + + frame.popn(2); + frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, result); + return Compile_Okay; + } + } + + return Compile_Skipped; +} + bool mjit::Compiler::jsop_equality(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused) { @@ -487,46 +539,11 @@ mjit::Compiler::jsop_equality(JSOp op, BoolStub stub, jsbytecode *target, JSOp f } if (cx->typeInferenceEnabled() && - lhs->isType(JSVAL_TYPE_OBJECT) && rhs->isType(JSVAL_TYPE_OBJECT)) { - /* - * Handle equality between two objects. We have to ensure there is no - * special equality operator on either object, if that passes then - * this is a pointer comparison. - */ - types::TypeSet *lhsTypes = analysis->poppedTypes(PC, 1); - types::TypeSet *rhsTypes = analysis->poppedTypes(PC, 0); - if (!lhsTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPECIAL_EQUALITY) && - !rhsTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPECIAL_EQUALITY)) { - /* :TODO: Merge with jsop_relational_int? */ - JS_ASSERT_IF(!target, fused != JSOP_IFEQ); - frame.forgetMismatchedObject(lhs); - frame.forgetMismatchedObject(rhs); - Assembler::Condition cond = GetCompareCondition(op, fused); - if (target) { - Jump sj = stubcc.masm.branchTest32(GetStubCompareCondition(fused), - Registers::ReturnReg, Registers::ReturnReg); - if (!frame.syncForBranch(target, Uses(2))) - return false; - RegisterID lreg = frame.tempRegForData(lhs); - frame.pinReg(lreg); - RegisterID rreg = frame.tempRegForData(rhs); - frame.unpinReg(lreg); - Jump fast = masm.branchPtr(cond, lreg, rreg); - frame.popn(2); - return jumpAndTrace(fast, target, &sj); - } else { - RegisterID result = frame.allocReg(); - RegisterID lreg = frame.tempRegForData(lhs); - frame.pinReg(lreg); - RegisterID rreg = frame.tempRegForData(rhs); - frame.unpinReg(lreg); - masm.branchValue(cond, lreg, rreg, result); - - frame.popn(2); - frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, result); - return true; - } - } + lhs->isType(JSVAL_TYPE_OBJECT) && rhs->isType(JSVAL_TYPE_OBJECT)) + { + CompileStatus status = jsop_equality_obj_obj(op, target, fused); + if (status == Compile_Okay) return true; + else if (status == Compile_Error) return false; } return emitStubCmpOp(stub, target, fused); @@ -2349,6 +2366,92 @@ mjit::Compiler::jsop_stricteq(JSOp op) return; } + if (lhs->isType(JSVAL_TYPE_STRING) || rhs->isType(JSVAL_TYPE_STRING)) { + FrameEntry *maybeNotStr = lhs->isType(JSVAL_TYPE_STRING) ? rhs : lhs; + + if (maybeNotStr->isNotType(JSVAL_TYPE_STRING)) { + frame.popn(2); + frame.push(BooleanValue(false)); + return; + } + + if (!maybeNotStr->isTypeKnown()) { + JS_ASSERT(!maybeNotStr->isConstant()); + Jump j = frame.testString(Assembler::NotEqual, maybeNotStr); + stubcc.linkExit(j, Uses(2)); + } + + FrameEntry *op1 = lhs->isConstant() ? rhs : lhs; + FrameEntry *op2 = lhs->isConstant() ? lhs : rhs; + JS_ASSERT(!op1->isConstant()); + + /* ReturnReg is safely usable with set32, since %ah can be accessed. */ + RegisterID resultReg = Registers::ReturnReg; + frame.takeReg(resultReg); + RegisterID tmpReg = frame.allocReg(); + RegisterID reg1 = frame.tempRegForData(op1); + frame.pinReg(reg1); + + RegisterID reg2; + if (op2->isConstant()) { + reg2 = frame.allocReg(); + JSString *str = op2->getValue().toString(); + JS_ASSERT(str->isAtom()); + masm.move(ImmPtr(str), reg2); + } else { + reg2 = frame.tempRegForData(op2); + frame.pinReg(reg2); + } + + JS_ASSERT(reg1 != resultReg); + JS_ASSERT(reg1 != tmpReg); + JS_ASSERT(reg2 != resultReg); + JS_ASSERT(reg2 != tmpReg); + + /* JSString::isAtom === (lengthAndFlags & ATOM_MASK == 0) */ + JS_STATIC_ASSERT(JSString::ATOM_FLAGS == 0); + Imm32 atomMask(JSString::ATOM_MASK); + + masm.load32(Address(reg1, JSString::offsetOfLengthAndFlags()), tmpReg); + Jump op1NotAtomized = masm.branchTest32(Assembler::NonZero, tmpReg, atomMask); + stubcc.linkExit(op1NotAtomized, Uses(2)); + + if (!op2->isConstant()) { + masm.load32(Address(reg2, JSString::offsetOfLengthAndFlags()), tmpReg); + Jump op2NotAtomized = masm.branchTest32(Assembler::NonZero, tmpReg, atomMask); + stubcc.linkExit(op2NotAtomized, Uses(2)); + } + + masm.set32(cond, reg1, reg2, resultReg); + + frame.unpinReg(reg1); + if (op2->isConstant()) + frame.freeReg(reg2); + else + frame.unpinReg(reg2); + frame.freeReg(tmpReg); + + stubcc.leave(); + if (op == JSOP_STRICTEQ) + OOL_STUBCALL_USES(stubs::StrictEq, REJOIN_NONE, Uses(2)); + else + OOL_STUBCALL_USES(stubs::StrictNe, REJOIN_NONE, Uses(2)); + + frame.popn(2); + frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, resultReg); + + stubcc.rejoin(Changes(1)); + return; + } + + if (cx->typeInferenceEnabled() && + lhs->isType(JSVAL_TYPE_OBJECT) && rhs->isType(JSVAL_TYPE_OBJECT)) + { + CompileStatus status = jsop_equality_obj_obj(op, NULL, JSOP_NOP); + if (status == Compile_Okay) return; + JS_ASSERT(status == Compile_Skipped); + } + /* Is it impossible that both Values are ints? */ if ((lhs->isTypeKnown() && lhs->isNotType(JSVAL_TYPE_INT32)) || (rhs->isTypeKnown() && rhs->isNotType(JSVAL_TYPE_INT32))) { diff --git a/js/src/methodjit/InvokeHelpers.cpp b/js/src/methodjit/InvokeHelpers.cpp index b8531678061c..0338910ecd9d 100644 --- a/js/src/methodjit/InvokeHelpers.cpp +++ b/js/src/methodjit/InvokeHelpers.cpp @@ -1562,7 +1562,7 @@ js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VM JS_NOT_REACHED("Bad branch op"); } if (takeBranch) - f.regs.pc = nextpc + GET_JUMP_OFFSET(nextpc); + f.regs.pc = nextpc + analyze::GetJumpOffset(nextpc, nextpc); else f.regs.pc = nextpc + analyze::GetBytecodeLength(nextpc); break; diff --git a/js/src/tests/ecma_5/Array/jstests.list b/js/src/tests/ecma_5/Array/jstests.list index 7924ca7aaad1..9e0b5c419811 100644 --- a/js/src/tests/ecma_5/Array/jstests.list +++ b/js/src/tests/ecma_5/Array/jstests.list @@ -3,6 +3,8 @@ script length-01.js script length-set-object.js script regress-599159.js script sort-01.js +script splice-return-array-elements-defined-not-set.js +script splice-suppresses-unvisited-indexes.js script toLocaleString-01.js script toString-01.js script unshift-01.js diff --git a/js/src/tests/ecma_5/Array/splice-return-array-elements-defined-not-set.js b/js/src/tests/ecma_5/Array/splice-return-array-elements-defined-not-set.js new file mode 100644 index 000000000000..2f5eca610dfc --- /dev/null +++ b/js/src/tests/ecma_5/Array/splice-return-array-elements-defined-not-set.js @@ -0,0 +1,46 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 668024; +var summary = + 'Array.prototype.splice should define, not set, the elements of the array ' + + 'it returns'; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +Object.defineProperty(Object.prototype, 2, + { + set: function(v) + { + throw new Error("setter on Object.prototype called!"); + }, + get: function() { return "fnord"; }, + enumerable: false, + configurable: true + }); + +var arr = [0, 1, 2, 3, 4, 5]; +var removed = arr.splice(0, 6); + +assertEq(arr.length, 0); +assertEq(removed.length, 6); +assertEq(removed[0], 0); +assertEq(removed[1], 1); +assertEq(removed[2], 2); +assertEq(removed[3], 3); +assertEq(removed[4], 4); +assertEq(removed[5], 5); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/ecma_5/Array/splice-suppresses-unvisited-indexes.js b/js/src/tests/ecma_5/Array/splice-suppresses-unvisited-indexes.js new file mode 100644 index 000000000000..717116fa0a69 --- /dev/null +++ b/js/src/tests/ecma_5/Array/splice-suppresses-unvisited-indexes.js @@ -0,0 +1,61 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 668024; +var summary = + 'Array.prototype.splice, when it deletes elements, should make sure any ' + + 'deleted but not visited elements are suppressed from subsequent enumeration'; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var arr = [0, 1, 2, 3, 4, 5, , 7]; + +var seen = []; +var sawOneBeforeThree = true; +for (var p in arr) +{ + if (p === "1") + { + // The order of enumeration of properties is unspecified, so technically, + // it would be kosher to enumerate "1" last, say, such that all properties + // in the array actually were enumerated, including an index which splice + // would delete. Don't flag that case as a failure. (SpiderMonkey doesn't + // do this, and neither do any of the other browser engines, but it is + // permissible behavior.) + if (seen.indexOf("3") >= 0) + { + sawOneBeforeThree = false; + break; + } + + arr.splice(2, 3); + } + + seen.push(p); +} + +if (sawOneBeforeThree) +{ + // ES5 12.6.4 states: + // + // If a property that has not yet been visited during enumeration is + // deleted, then it will not be visited. + // + // So if we haven't seen "3" by the time we see "1", the splice call above + // will delete "3", and therefore we must not see it. + assertEq(seen.indexOf("3"), -1); +} + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/ecma_5/extensions/getOwnPropertyNames-__proto__.js b/js/src/tests/ecma_5/extensions/getOwnPropertyNames-__proto__.js new file mode 100644 index 000000000000..764e0dcc2916 --- /dev/null +++ b/js/src/tests/ecma_5/extensions/getOwnPropertyNames-__proto__.js @@ -0,0 +1,28 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 690031; +var summary = + 'Exclude __proto__ from showing up when enumerating properties of ' + + 'Object.prototype again'; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var keys = Object.getOwnPropertyNames(Object.prototype); +assertEq(keys.indexOf("__proto__"), -1, + "shouldn't have gotten __proto__ as a property of Object.prototype " + + "(got these properties: " + keys + ")"); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/ecma_5/extensions/jstests.list b/js/src/tests/ecma_5/extensions/jstests.list index 44a7486f9f8b..3f7f590dce15 100644 --- a/js/src/tests/ecma_5/extensions/jstests.list +++ b/js/src/tests/ecma_5/extensions/jstests.list @@ -15,6 +15,7 @@ script eval-native-callback-is-indirect.js script extension-methods-reject-null-undefined-this.js skip-if(!xulRuntime.shell) script function-definition-with.js # needs evaluate() script function-properties.js +script getOwnPropertyNames-__proto__.js script iterator-in-catch.js script JSON-string-replacer-overflow.js skip-if(!xulRuntime.shell) script legacy-JSON.js # needs parseLegacyJSON diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 754d0bfc1a1f..2d5fac107d37 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -312,7 +312,7 @@ Breakpoint::nextInSite() Debugger::Debugger(JSContext *cx, JSObject *dbg) : object(dbg), uncaughtExceptionHook(NULL), enabled(true), - frames(cx), objects(cx), heldScripts(cx), nonHeldScripts(cx) + frames(cx), objects(cx), scripts(cx) { assertSameCompartment(cx, dbg); @@ -334,11 +334,10 @@ Debugger::~Debugger() bool Debugger::init(JSContext *cx) { - bool ok = (frames.init() && - objects.init() && - debuggees.init() && - heldScripts.init() && - nonHeldScripts.init()); + bool ok = frames.init() && + objects.init() && + debuggees.init() && + scripts.init(); if (!ok) js_ReportOutOfMemory(cx); return ok; @@ -405,13 +404,7 @@ Debugger::hasAnyLiveHooks(JSContext *cx) const /* If any breakpoints are in live scripts, return true. */ for (Breakpoint *bp = firstBreakpoint(); bp; bp = bp->nextInDebugger()) { - /* - * If holder is non-null, examine it to see if the script will be - * collected. If holder is null, then bp->site->script is an eval - * script on the stack, so it is definitely live. - */ - JSObject *holder = bp->site->getScriptObject(); - if (!holder || !IsAboutToBeFinalized(cx, holder)) + if (!IsAboutToBeFinalized(cx, bp->site->script)) return true; } @@ -494,7 +487,7 @@ Debugger::wrapDebuggeeValue(JSContext *cx, Value *vp) if (vp->isObject()) { JSObject *obj = &vp->toObject(); - ObjectWeakMap::AddPtr p = objects.lookupForAdd(obj); + CellWeakMap::AddPtr p = objects.lookupForAdd(obj); if (p) { vp->setObject(*p->value); } else { @@ -738,7 +731,7 @@ Debugger::fireEnterFrame(JSContext *cx) } void -Debugger::fireNewScript(JSContext *cx, JSScript *script, JSObject *obj, NewScriptKind kind) +Debugger::fireNewScript(JSContext *cx, JSScript *script, JSObject *obj) { JSObject *hook = getHook(OnNewScript); JS_ASSERT(hook); @@ -748,8 +741,7 @@ Debugger::fireNewScript(JSContext *cx, JSScript *script, JSObject *obj, NewScrip if (!ac.enter()) return; - JSObject *dsobj = - kind == NewHeldScript ? wrapHeldScript(cx, script, obj) : wrapNonHeldScript(cx, script); + JSObject *dsobj = wrapScript(cx, script, obj); if (!dsobj) { handleUncaughtException(ac, NULL, false); return; @@ -822,8 +814,11 @@ AddNewScriptRecipients(GlobalObject::DebuggerVector *src, AutoValueVector *dest) } void -Debugger::slowPathOnNewScript(JSContext *cx, JSScript *script, JSObject *obj, NewScriptKind kind) +Debugger::slowPathOnNewScript(JSContext *cx, JSScript *script, JSObject *obj, + GlobalObject *compileAndGoGlobal) { + JS_ASSERT(script->compileAndGo == !!compileAndGoGlobal); + /* * Build the list of recipients. For compile-and-go scripts, this is the * same as the generic Debugger::dispatchHook code, but non-compile-and-go @@ -831,15 +826,12 @@ Debugger::slowPathOnNewScript(JSContext *cx, JSScript *script, JSObject *obj, Ne * debugger observing any global in the script's compartment. */ AutoValueVector triggered(cx); - GlobalObject *global; if (script->compileAndGo) { - global = obj->getGlobal(); - if (GlobalObject::DebuggerVector *debuggers = global->getDebuggers()) { + if (GlobalObject::DebuggerVector *debuggers = compileAndGoGlobal->getDebuggers()) { if (!AddNewScriptRecipients(debuggers, &triggered)) return; } } else { - global = NULL; GlobalObjectSet &debuggees = script->compartment()->getDebuggees(); for (GlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) { if (!AddNewScriptRecipients(r.front()->getDebuggers(), &triggered)) @@ -853,8 +845,10 @@ Debugger::slowPathOnNewScript(JSContext *cx, JSScript *script, JSObject *obj, Ne */ for (Value *p = triggered.begin(); p != triggered.end(); p++) { Debugger *dbg = Debugger::fromJSObject(&p->toObject()); - if ((!global || dbg->debuggees.has(global)) && dbg->enabled && dbg->getHook(OnNewScript)) - dbg->fireNewScript(cx, script, obj, kind); + if ((!compileAndGoGlobal || dbg->debuggees.has(compileAndGoGlobal)) && + dbg->enabled && dbg->getHook(OnNewScript)) { + dbg->fireNewScript(cx, script, obj); + } } } @@ -1011,7 +1005,7 @@ Debugger::onSingleStep(JSContext *cx, Value *vp) /*** Debugger JSObjects **************************************************************************/ void -Debugger::markKeysInCompartment(JSTracer *tracer, const ObjectWeakMap &map) +Debugger::markKeysInCompartment(JSTracer *tracer, const CellWeakMap &map, bool scripts) { JSCompartment *comp = tracer->context->runtime->gcCurrentCompartment; JS_ASSERT(comp); @@ -1021,12 +1015,19 @@ Debugger::markKeysInCompartment(JSTracer *tracer, const ObjectWeakMap &map) * enumerating WeakMap keys. However in this case we need access, so we * make a base-class reference. Range is public in HashMap. */ - typedef HashMap, RuntimeAllocPolicy> Map; + typedef HashMap, RuntimeAllocPolicy> Map; const Map &storage = map; for (Map::Range r = storage.all(); !r.empty(); r.popFront()) { - JSObject *key = r.front().key; - if (key->compartment() == comp && IsAboutToBeFinalized(tracer->context, key)) - js::gc::MarkObject(tracer, *key, "cross-compartment WeakMap key"); + gc::Cell *key = r.front().key; + if (key->compartment() == comp && IsAboutToBeFinalized(tracer->context, key)) { + if (scripts) { + js::gc::MarkScript(tracer, static_cast(key), + "cross-compartment WeakMap key"); + } else { + js::gc::MarkObject(tracer, *static_cast(key), + "cross-compartment WeakMap key"); + } + } } } @@ -1042,7 +1043,7 @@ Debugger::markKeysInCompartment(JSTracer *tracer, const ObjectWeakMap &map) * manually. * * Each Debugger object keeps two cross-compartment WeakMaps: objects and - * heldScripts. Both have the nice property that all their values are in the + * scripts. Both have the nice property that all their values are in the * same compartment as the Debugger object, so we only need to mark the * keys. We must simply mark all keys that are in the compartment being GC'd. * @@ -1066,8 +1067,8 @@ Debugger::markCrossCompartmentDebuggerObjectReferents(JSTracer *tracer) for (JSCList *p = &rt->debuggerList; (p = JS_NEXT_LINK(p)) != &rt->debuggerList;) { Debugger *dbg = Debugger::fromLinks(p); if (dbg->object->compartment() != comp) { - markKeysInCompartment(tracer, dbg->objects); - markKeysInCompartment(tracer, dbg->heldScripts); + markKeysInCompartment(tracer, dbg->objects, false); + markKeysInCompartment(tracer, dbg->scripts, true); } } } @@ -1145,8 +1146,7 @@ Debugger::markAllIteratively(GCMarker *trc) if (dbgMarked) { /* Search for breakpoints to mark. */ for (Breakpoint *bp = dbg->firstBreakpoint(); bp; bp = bp->nextInDebugger()) { - JSObject *scriptObject = bp->site->getScriptObject(); - if (!scriptObject || !IsAboutToBeFinalized(cx, scriptObject)) { + if (!IsAboutToBeFinalized(cx, bp->site->script)) { /* * The debugger and the script are both live. * Therefore the breakpoint handler is live. @@ -1195,23 +1195,8 @@ Debugger::trace(JSTracer *trc) /* Trace the referent -> Debugger.Object weak map. */ objects.trace(trc); - /* - * Trace the weak map from JSFunctions and "Script" JSObjects to - * Debugger.Script objects. - */ - heldScripts.trace(trc); - - /* Trace the map for non-held scripts, which are explicitly freed. */ - for (ScriptMap::Range r = nonHeldScripts.all(); !r.empty(); r.popFront()) { - JSObject *scriptobj = r.front().value; - - /* - * nonHeldScripts should only refer to Debugger.Script objects for - * scripts that haven't been freed yet. - */ - JS_ASSERT(scriptobj->getPrivate()); - MarkObject(trc, *scriptobj, "live eval Debugger.Script"); - } + /* Trace the weak map from JSScript instances to Debugger.Script objects. */ + scripts.trace(trc); } void @@ -1791,45 +1776,6 @@ JSFunctionSpec Debugger::methods[] = { /*** Debugger.Script *****************************************************************************/ -/* - * JSScripts' lifetimes fall into to two categories: - * - * - "Held scripts": JSScripts belonging to JSFunctions and JSScripts created - * using JSAPI have lifetimes determined by the garbage collector. A JSScript - * itself has no mark bit of its own. Instead, its holding object manages the - * JSScript as part of its own structure: the holder has a mark bit; when the - * holder is marked it calls js_TraceScript on its JSScript; and when the - * holder is freed it explicitly frees its JSScript. - * - * Debugger.Script instances for held scripts are strong references to the - * holder (and thus to the script). Debugger::heldScripts weakly maps - * debuggee holding objects to the Debugger.Script objects for their - * JSScripts. We needn't act on a destroyScript event for a held script: if - * we get such an event we know its Debugger.Script is dead anyway, and its - * entry in Debugger::heldScripts will be cleaned up by the standard weak - * table code. - * - * - "Non-held scripts": JSScripts generated temporarily for a call to eval or - * JS_Evaluate*, live until the call completes, at which point the script is - * destroyed. - * - * A Debugger.Script instance for a non-held script has no influence on the - * JSScript's lifetime. Debugger::nonHeldScripts maps live JSScripts to to - * their Debugger.Script objects. When a destroyScript event tells us that - * a non-held script is dead, we remove its table entry, and clear its - * Debugger.Script object's script pointer, thus marking it dead. - * - * A Debugger.Script's private pointer points directly to the JSScript, or is - * NULL if the Debugger.Script is dead. The JSSLOT_DEBUGSCRIPT_HOLDER slot - * refers to the holding object, or is null for non-held JSScripts. The private - * pointer is not traced; the holding object reference, if present, is traced - * via DebuggerScript_trace. - * - * (We consider a script saved in and retrieved from the eval cache to have - * been destroyed, and then --- mirabile dictu --- re-created at the same - * address. The newScriptHook and destroyScriptHook hooks cooperate with this - * view.) - */ static inline JSScript * GetScriptReferent(JSObject *obj) { @@ -1837,13 +1783,6 @@ GetScriptReferent(JSObject *obj) return (JSScript *) obj->getPrivate(); } -static inline void -ClearScriptReferent(JSObject *obj) -{ - JS_ASSERT(obj->getClass() == &DebuggerScript_class); - obj->setPrivate(NULL); -} - static inline JSObject * GetScriptHolder(JSObject *obj) { @@ -1856,6 +1795,8 @@ static void DebuggerScript_trace(JSTracer *trc, JSObject *obj) { if (!trc->context->runtime->gcCurrentCompartment) { + if (JSScript *script = GetScriptReferent(obj)) + MarkScript(trc, script, "Debugger.Script referent"); Value v = obj->getReservedSlot(JSSLOT_DEBUGSCRIPT_HOLDER); if (!v.isUndefined()) { if (JSObject *obj = (JSObject *) v.toPrivate()) @@ -1895,18 +1836,18 @@ Debugger::newDebuggerScript(JSContext *cx, JSScript *script, JSObject *holder) } JSObject * -Debugger::wrapHeldScript(JSContext *cx, JSScript *script, JSObject *obj) +Debugger::wrapScript(JSContext *cx, JSScript *script, JSObject *obj) { assertSameCompartment(cx, object); JS_ASSERT(cx->compartment != script->compartment()); - JS_ASSERT(script->compartment() == obj->compartment()); + JS_ASSERT_IF(obj, script->compartment() == obj->compartment()); - ScriptWeakMap::AddPtr p = heldScripts.lookupForAdd(obj); + CellWeakMap::AddPtr p = scripts.lookupForAdd(script); if (!p) { JSObject *scriptobj = newDebuggerScript(cx, script, obj); /* The allocation may have caused a GC, which can remove table entries. */ - if (!scriptobj || !heldScripts.relookupOrAdd(p, obj, scriptobj)) + if (!scriptobj || !scripts.relookupOrAdd(p, script, scriptobj)) return NULL; } @@ -1917,61 +1858,11 @@ Debugger::wrapHeldScript(JSContext *cx, JSScript *script, JSObject *obj) JSObject * Debugger::wrapFunctionScript(JSContext *cx, JSFunction *fun) { - return wrapHeldScript(cx, fun->script(), fun); -} - -JSObject * -Debugger::wrapJSAPIScript(JSContext *cx, JSObject *obj) -{ - JS_ASSERT(obj->isScript()); - return wrapHeldScript(cx, obj->getScript(), obj); -} - -JSObject * -Debugger::wrapNonHeldScript(JSContext *cx, JSScript *script) -{ - assertSameCompartment(cx, object); - JS_ASSERT(cx->compartment != script->compartment()); - - ScriptMap::AddPtr p = nonHeldScripts.lookupForAdd(script); - if (!p) { - JSObject *scriptobj = newDebuggerScript(cx, script, NULL); - - /* The allocation may have caused a GC, which can remove table entries. */ - if (!scriptobj || !nonHeldScripts.relookupOrAdd(p, script, scriptobj)) - return NULL; - } - - JS_ASSERT(GetScriptReferent(p->value) == script); - return p->value; -} - -void -Debugger::slowPathOnDestroyScript(JSScript *script) -{ - /* Find all debuggers that might have Debugger.Script referring to this script. */ - js::GlobalObjectSet *debuggees = &script->compartment()->getDebuggees(); - for (GlobalObjectSet::Range r = debuggees->all(); !r.empty(); r.popFront()) { - GlobalObject::DebuggerVector *debuggers = r.front()->getDebuggers(); - for (Debugger **p = debuggers->begin(); p != debuggers->end(); p++) - (*p)->destroyNonHeldScript(script); - } -} - -void -Debugger::destroyNonHeldScript(JSScript *script) -{ - ScriptMap::Ptr p = nonHeldScripts.lookup(script); - if (p) { - JS_ASSERT(GetScriptReferent(p->value) == script); - ClearScriptReferent(p->value); - nonHeldScripts.remove(p); - } + return wrapScript(cx, fun->script(), fun); } static JSObject * -DebuggerScript_check(JSContext *cx, const Value &v, const char *clsname, const char *fnname, - bool checkLive) +DebuggerScript_check(JSContext *cx, const Value &v, const char *clsname, const char *fnname) { if (!v.isObject()) { ReportObjectRequired(cx); @@ -1989,42 +1880,33 @@ DebuggerScript_check(JSContext *cx, const Value &v, const char *clsname, const c * but whose holding object is undefined. */ if (thisobj->getReservedSlot(JSSLOT_DEBUGSCRIPT_HOLDER).isUndefined()) { + JS_ASSERT(!GetScriptReferent(thisobj)); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO, clsname, fnname, "prototype object"); return NULL; } - - if (checkLive && !GetScriptReferent(thisobj)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_NOT_LIVE, - clsname, fnname, "script"); - return NULL; - } + JS_ASSERT(GetScriptReferent(thisobj)); return thisobj; } static JSObject * -DebuggerScript_checkThis(JSContext *cx, const CallArgs &args, const char *fnname, bool checkLive) +DebuggerScript_checkThis(JSContext *cx, const CallArgs &args, const char *fnname) { - return DebuggerScript_check(cx, args.thisv(), "Debugger.Script", fnname, checkLive); + return DebuggerScript_check(cx, args.thisv(), "Debugger.Script", fnname); } -#define THIS_DEBUGSCRIPT_SCRIPT_NEEDLIVE(cx, argc, vp, fnname, args, obj, script, checkLive) \ +#define THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, fnname, args, obj, script) \ CallArgs args = CallArgsFromVp(argc, vp); \ - JSObject *obj = DebuggerScript_checkThis(cx, args, fnname, checkLive); \ + JSObject *obj = DebuggerScript_checkThis(cx, args, fnname); \ if (!obj) \ return false; \ JSScript *script = GetScriptReferent(obj) -#define THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, fnname, args, obj, script) \ - THIS_DEBUGSCRIPT_SCRIPT_NEEDLIVE(cx, argc, vp, fnname, args, obj, script, false) -#define THIS_DEBUGSCRIPT_LIVE_SCRIPT(cx, argc, vp, fnname, args, obj, script) \ - THIS_DEBUGSCRIPT_SCRIPT_NEEDLIVE(cx, argc, vp, fnname, args, obj, script, true) - static JSBool DebuggerScript_getUrl(JSContext *cx, uintN argc, Value *vp) { - THIS_DEBUGSCRIPT_LIVE_SCRIPT(cx, argc, vp, "get url", args, obj, script); + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getUrl", args, obj, script); JSString *str = js_NewStringCopyZ(cx, script->filename); if (!str) @@ -2036,7 +1918,7 @@ DebuggerScript_getUrl(JSContext *cx, uintN argc, Value *vp) static JSBool DebuggerScript_getStartLine(JSContext *cx, uintN argc, Value *vp) { - THIS_DEBUGSCRIPT_LIVE_SCRIPT(cx, argc, vp, "get startLine", args, obj, script); + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getStartLine", args, obj, script); args.rval().setNumber(script->lineno); return true; } @@ -2044,25 +1926,17 @@ DebuggerScript_getStartLine(JSContext *cx, uintN argc, Value *vp) static JSBool DebuggerScript_getLineCount(JSContext *cx, uintN argc, Value *vp) { - THIS_DEBUGSCRIPT_LIVE_SCRIPT(cx, argc, vp, "get lineCount", args, obj, script); + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getLineCount", args, obj, script); uintN maxLine = js_GetScriptLineExtent(script); args.rval().setNumber(jsdouble(maxLine)); return true; } -static JSBool -DebuggerScript_getLive(JSContext *cx, uintN argc, Value *vp) -{ - THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "get live", args, obj, script); - args.rval().setBoolean(!!script); - return true; -} - static JSBool DebuggerScript_getChildScripts(JSContext *cx, uintN argc, Value *vp) { - THIS_DEBUGSCRIPT_LIVE_SCRIPT(cx, argc, vp, "get live", args, obj, script); + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getChildScripts", args, obj, script); Debugger *dbg = Debugger::fromChildJSObject(obj); JSObject *result = NewDenseEmptyArray(cx); @@ -2111,7 +1985,7 @@ static JSBool DebuggerScript_getOffsetLine(JSContext *cx, uintN argc, Value *vp) { REQUIRE_ARGC("Debugger.Script.getOffsetLine", 1); - THIS_DEBUGSCRIPT_LIVE_SCRIPT(cx, argc, vp, "getOffsetLine", args, obj, script); + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getOffsetLine", args, obj, script); size_t offset; if (!ScriptOffset(cx, script, args[0], &offset)) return false; @@ -2271,7 +2145,7 @@ class FlowGraphSummary : public Vector { static JSBool DebuggerScript_getAllOffsets(JSContext *cx, uintN argc, Value *vp) { - THIS_DEBUGSCRIPT_LIVE_SCRIPT(cx, argc, vp, "getAllOffsets", args, obj, script); + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getAllOffsets", args, obj, script); /* * First pass: determine which offsets in this script are jump targets and @@ -2329,7 +2203,7 @@ DebuggerScript_getAllOffsets(JSContext *cx, uintN argc, Value *vp) static JSBool DebuggerScript_getLineOffsets(JSContext *cx, uintN argc, Value *vp) { - THIS_DEBUGSCRIPT_LIVE_SCRIPT(cx, argc, vp, "getAllOffsets", args, obj, script); + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getLineOffsets", args, obj, script); REQUIRE_ARGC("Debugger.Script.getLineOffsets", 1); /* Parse lineno argument. */ @@ -2378,7 +2252,7 @@ static JSBool DebuggerScript_setBreakpoint(JSContext *cx, uintN argc, Value *vp) { REQUIRE_ARGC("Debugger.Script.setBreakpoint", 2); - THIS_DEBUGSCRIPT_LIVE_SCRIPT(cx, argc, vp, "setBreakpoint", args, obj, script); + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "setBreakpoint", args, obj, script); Debugger *dbg = Debugger::fromChildJSObject(obj); JSObject *holder = GetScriptHolder(obj); @@ -2414,7 +2288,7 @@ DebuggerScript_setBreakpoint(JSContext *cx, uintN argc, Value *vp) static JSBool DebuggerScript_getBreakpoints(JSContext *cx, uintN argc, Value *vp) { - THIS_DEBUGSCRIPT_LIVE_SCRIPT(cx, argc, vp, "getBreakpoints", args, obj, script); + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getBreakpoints", args, obj, script); Debugger *dbg = Debugger::fromChildJSObject(obj); jsbytecode *pc; @@ -2451,7 +2325,7 @@ static JSBool DebuggerScript_clearBreakpoint(JSContext *cx, uintN argc, Value *vp) { REQUIRE_ARGC("Debugger.Script.clearBreakpoint", 1); - THIS_DEBUGSCRIPT_LIVE_SCRIPT(cx, argc, vp, "clearBreakpoint", args, obj, script); + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "clearBreakpoint", args, obj, script); Debugger *dbg = Debugger::fromChildJSObject(obj); JSObject *handler = NonNullObject(cx, args[0]); @@ -2466,7 +2340,7 @@ DebuggerScript_clearBreakpoint(JSContext *cx, uintN argc, Value *vp) static JSBool DebuggerScript_clearAllBreakpoints(JSContext *cx, uintN argc, Value *vp) { - THIS_DEBUGSCRIPT_LIVE_SCRIPT(cx, argc, vp, "clearBreakpoint", args, obj, script); + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "clearAllBreakpoints", args, obj, script); Debugger *dbg = Debugger::fromChildJSObject(obj); script->compartment()->clearBreakpointsIn(cx, dbg, script, NULL); args.rval().setUndefined(); @@ -2484,7 +2358,6 @@ static JSPropertySpec DebuggerScript_properties[] = { JS_PSG("url", DebuggerScript_getUrl, 0), JS_PSG("startLine", DebuggerScript_getStartLine, 0), JS_PSG("lineCount", DebuggerScript_getLineCount, 0), - JS_PSG("live", DebuggerScript_getLive, 0), JS_PS_END }; @@ -2759,17 +2632,12 @@ DebuggerFrame_getScript(JSContext *cx, uintN argc, Value *vp) } } else if (fp->isScriptFrame()) { /* - * eval, JS_Evaluate*, and JS_ExecuteScript all create non-function - * script frames. However, scripts for JS_ExecuteScript are held by - * script objects, and must go in heldScripts, whereas scripts for eval - * and JS_Evaluate* latter are explicitly destroyed when the call - * returns, and must go in nonHeldScripts. Distinguish the two cases by - * checking whether the script has a Script object allocated to it. + * We got eval, JS_Evaluate*, or JS_ExecuteScript non-function script + * frames. */ JSScript *script = fp->script(); - scriptObject = (script->u.object) - ? debug->wrapJSAPIScript(cx, script->u.object) - : debug->wrapNonHeldScript(cx, script); + scriptObject = debug->wrapScript(cx, script, + script->isCachedEval ? NULL : script->u.object); if (!scriptObject) return false; } diff --git a/js/src/vm/Debugger.h b/js/src/vm/Debugger.h index 9457dc3384af..b10f698d1bb3 100644 --- a/js/src/vm/Debugger.h +++ b/js/src/vm/Debugger.h @@ -59,8 +59,6 @@ class Debugger { friend JSBool (::JS_DefineDebuggerObject)(JSContext *cx, JSObject *obj); public: - enum NewScriptKind { NewNonHeldScript, NewHeldScript }; - enum Hook { OnDebuggerStatement, OnExceptionUnwind, @@ -105,41 +103,14 @@ class Debugger { FrameMap; FrameMap frames; + typedef WeakMap, CrossCompartmentMarkPolicy> + CellWeakMap; + /* The map from debuggee objects to their Debugger.Object instances. */ - typedef WeakMap, CrossCompartmentMarkPolicy> - ObjectWeakMap; - ObjectWeakMap objects; + CellWeakMap objects; - /* - * An ephemeral map from script-holding objects to Debugger.Script - * instances. - */ - typedef WeakMap, CrossCompartmentMarkPolicy> - ScriptWeakMap; - - /* - * Map of Debugger.Script instances for garbage-collected JSScripts. For - * function scripts, the key is the compiler-created, internal JSFunction; - * for scripts returned by JSAPI functions, the key is the "Script"-class - * JSObject. - */ - ScriptWeakMap heldScripts; - - /* - * An ordinary (non-ephemeral) map from JSScripts to Debugger.Script - * instances, for non-held scripts that are explicitly freed. - */ - typedef HashMap, RuntimeAllocPolicy> - ScriptMap; - - /* - * Map from non-held JSScripts to their Debugger.Script objects. Non-held - * scripts are scripts created for eval or JS_Evaluate* calls that are - * explicitly destroyed when the call returns. Debugger.Script objects are - * not strong references to such JSScripts; the Debugger.Script becomes - * "dead" when the eval call returns. - */ - ScriptMap nonHeldScripts; + /* An ephemeral map from JSScript* to Debugger.Script instances. */ + CellWeakMap scripts; bool addDebuggeeGlobal(JSContext *cx, GlobalObject *obj); void removeDebuggeeGlobal(JSContext *cx, GlobalObject *global, @@ -195,7 +166,7 @@ class Debugger { static void traceObject(JSTracer *trc, JSObject *obj); void trace(JSTracer *trc); static void finalize(JSContext *cx, JSObject *obj); - static void markKeysInCompartment(JSTracer *tracer, const ObjectWeakMap &map); + static void markKeysInCompartment(JSTracer *tracer, const CellWeakMap &map, bool scripts); static Class jsclass; @@ -230,9 +201,7 @@ class Debugger { static void slowPathOnEnterFrame(JSContext *cx); static void slowPathOnLeaveFrame(JSContext *cx); static void slowPathOnNewScript(JSContext *cx, JSScript *script, JSObject *obj, - NewScriptKind kind); - static void slowPathOnDestroyScript(JSScript *script); - + GlobalObject *compileAndGoGlobal); static JSTrapStatus dispatchHook(JSContext *cx, js::Value *vp, Hook which); JSTrapStatus fireDebuggerStatement(JSContext *cx, Value *vp); @@ -246,21 +215,12 @@ class Debugger { */ JSObject *newDebuggerScript(JSContext *cx, JSScript *script, JSObject *obj); - /* Helper function for wrapFunctionScript and wrapJSAPIscript. */ - JSObject *wrapHeldScript(JSContext *cx, JSScript *script, JSObject *obj); - /* * Receive a "new script" event from the engine. A new script was compiled - * or deserialized. If kind is NewHeldScript, obj must be the holder - * object. Otherwise, kind must be NewNonHeldScript, script must be an eval - * or JS_Evaluate* script, and we must have - * obj->getGlobal() == scopeObj->getGlobal() - * where scopeObj is the scope in which the new script will be executed. + * or deserialized. For eval scripts obj must be null, otherwise it must be + * a script object. */ - void fireNewScript(JSContext *cx, JSScript *script, JSObject *obj, NewScriptKind kind); - - /* Remove script from our table of non-held scripts. */ - void destroyNonHeldScript(JSScript *script); + void fireNewScript(JSContext *cx, JSScript *script, JSObject *obj); static inline Debugger *fromLinks(JSCList *links); inline Breakpoint *firstBreakpoint() const; @@ -302,8 +262,7 @@ class Debugger { static inline JSTrapStatus onDebuggerStatement(JSContext *cx, js::Value *vp); static inline JSTrapStatus onExceptionUnwind(JSContext *cx, js::Value *vp); static inline void onNewScript(JSContext *cx, JSScript *script, JSObject *obj, - NewScriptKind kind); - static inline void onDestroyScript(JSScript *script); + GlobalObject *compileAndGoGlobal); static JSTrapStatus onTrap(JSContext *cx, Value *vp); static JSTrapStatus onSingleStep(JSContext *cx, Value *vp); @@ -380,19 +339,12 @@ class Debugger { JSObject *wrapFunctionScript(JSContext *cx, JSFunction *fun); /* - * Return the Debugger.Script object for the Script object |obj|'s - * JSScript, or create a new one if needed. The context |cx| must be in the - * debugger compartment; |obj| must be a cross-compartment wrapper - * referring to a script object in a debuggee compartment. + * Return the Debugger.Script object for |script|, or create a new one if + * needed. The context |cx| must be in the debugger compartment; |script| must + * be a script in a debuggee compartment. |obj| is either the script holder or + * null for non-held scripts. */ - JSObject *wrapJSAPIScript(JSContext *cx, JSObject *scriptObj); - - /* - * Return the Debugger.Script object for the non-held script |script|, or - * create a new one if needed. The context |cx| must be in the debugger - * compartment; |script| must be a script in a debuggee compartment. - */ - JSObject *wrapNonHeldScript(JSContext *cx, JSScript *script); + JSObject *wrapScript(JSContext *cx, JSScript *script, JSObject *obj); private: /* Prohibit copying. */ @@ -413,7 +365,7 @@ class BreakpointSite { private: /* * The holder object for script, if known, else NULL. This is NULL for - * non-held scripts and for JSD1 traps. It is always non-null for JSD2 + * cached eval scripts and for JSD1 traps. It is always non-null for JSD2 * breakpoints in held scripts. */ JSObject *scriptObject; @@ -564,18 +516,13 @@ Debugger::onExceptionUnwind(JSContext *cx, js::Value *vp) } void -Debugger::onNewScript(JSContext *cx, JSScript *script, JSObject *obj, NewScriptKind kind) +Debugger::onNewScript(JSContext *cx, JSScript *script, JSObject *obj, + GlobalObject *compileAndGoGlobal) { - JS_ASSERT_IF(kind == NewHeldScript || script->compileAndGo, obj); + JS_ASSERT_IF(script->compileAndGo, compileAndGoGlobal); + JS_ASSERT_IF(!script->compileAndGo, !compileAndGoGlobal); if (!script->compartment()->getDebuggees().empty()) - slowPathOnNewScript(cx, script, obj, kind); -} - -void -Debugger::onDestroyScript(JSScript *script) -{ - if (!script->compartment()->getDebuggees().empty()) - slowPathOnDestroyScript(script); + slowPathOnNewScript(cx, script, obj, compileAndGoGlobal); } extern JSBool diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index 2514b3ec8e12..a317d02a2f88 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -57,8 +57,6 @@ using namespace mozilla::layers; namespace mozilla { -namespace { - /** * This is the userdata we associate with a layer manager. */ @@ -82,12 +80,14 @@ public: /** * Tracks which frames have layers associated with them. */ - nsTHashtable > mFramesWithLayers; + nsTHashtable mFramesWithLayers; bool mInvalidateAllLayers; /** Layer manager we belong to, we hold a reference to this object. */ nsRefPtr mLayerManager; }; +namespace { + static void DestroyRegion(void* aPropertyValue) { delete static_cast(aPropertyValue); @@ -470,39 +470,32 @@ FrameLayerBuilder::DisplayItemDataEntry::HasNonEmptyContainerLayer() return PR_FALSE; } -/* static */ void -FrameLayerBuilder::InternalDestroyDisplayItemData(nsIFrame* aFrame, - void* aPropertyValue, - bool aRemoveFromFramesWithLayers) +/* static */ nsTArray* +FrameLayerBuilder::GetDisplayItemDataArrayForFrame(nsIFrame* aFrame) { - nsRefPtr managerRef; - nsTArray* array = - reinterpret_cast*>(&aPropertyValue); - NS_ASSERTION(!array->IsEmpty(), "Empty arrays should not be stored"); + FrameProperties props = aFrame->Properties(); + LayerManagerData *data = + reinterpret_cast(props.Get(LayerManagerDataProperty())); + if (!data) + return nsnull; - if (aRemoveFromFramesWithLayers) { - LayerManager* manager = array->ElementAt(0).mLayer->Manager(); - LayerManagerData* data = static_cast - (manager->GetUserData(&gLayerManagerUserData)); - NS_ASSERTION(data, "Frame with layer should have been recorded"); - data->mFramesWithLayers.RemoveEntry(aFrame); - if (data->mFramesWithLayers.Count() == 0) { - // Destroying our user data will consume a reference from the layer - // manager. But don't actually release until we've released all the layers - // in the DisplayItemData array below! - managerRef = manager; - manager->RemoveUserData(&gLayerManagerUserData); - } - } + DisplayItemDataEntry *entry = data->mFramesWithLayers.GetEntry(aFrame); + NS_ASSERTION(entry, "out of sync?"); + if (!entry) + return nsnull; - array->~nsTArray(); + return &entry->mData; } /* static */ void -FrameLayerBuilder::DestroyDisplayItemData(nsIFrame* aFrame, - void* aPropertyValue) +FrameLayerBuilder::RemoveFrameFromLayerManager(nsIFrame* aFrame, + void* aPropertyValue) { - InternalDestroyDisplayItemData(aFrame, aPropertyValue, PR_TRUE); + LayerManagerData *data = reinterpret_cast(aPropertyValue); + data->mFramesWithLayers.RemoveEntry(aFrame); + if (data->mFramesWithLayers.Count() == 0) { + data->mLayerManager->RemoveUserData(&gLayerManagerUserData); + } } void @@ -606,7 +599,7 @@ SetNoContainerLayer(nsIFrame* aFrame) } /* static */ PLDHashOperator -FrameLayerBuilder::UpdateDisplayItemDataForFrame(nsPtrHashKey* aEntry, +FrameLayerBuilder::UpdateDisplayItemDataForFrame(DisplayItemDataEntry* aEntry, void* aUserArg) { FrameLayerBuilder* builder = static_cast(aUserArg); @@ -617,16 +610,8 @@ FrameLayerBuilder::UpdateDisplayItemDataForFrame(nsPtrHashKey* aEntry, if (!newDisplayItems) { // This frame was visible, but isn't anymore. bool found; - void* prop = props.Remove(DisplayItemDataProperty(), &found); + props.Remove(LayerManagerDataProperty(), &found); NS_ASSERTION(found, "How can the frame property be missing?"); - // Pass PR_FALSE to not remove from mFramesWithLayers, we'll remove it - // by returning PL_DHASH_REMOVE below. - // Note that DestroyDisplayItemData would delete the user data - // for the retained layer manager if it removed the last entry from - // mFramesWithLayers, but we won't. That's OK because our caller - // is DidEndTransaction, which would recreate the user data - // anyway. - InternalDestroyDisplayItemData(f, prop, PR_FALSE); SetNoContainerLayer(f); return PL_DHASH_REMOVE; } @@ -647,16 +632,8 @@ FrameLayerBuilder::UpdateDisplayItemDataForFrame(nsPtrHashKey* aEntry, SetNoContainerLayer(f); } - // We need to remove and re-add the DisplayItemDataProperty in - // case the nsTArray changes the value of its mHdr. - void* propValue = props.Remove(DisplayItemDataProperty()); - NS_ASSERTION(propValue, "mFramesWithLayers out of sync"); - PR_STATIC_ASSERT(sizeof(nsTArray) == sizeof(void*)); - nsTArray* array = - reinterpret_cast*>(&propValue); // Steal the list of display item layers - array->SwapElements(newDisplayItems->mData); - props.Set(DisplayItemDataProperty(), propValue); + aEntry->mData.SwapElements(newDisplayItems->mData); // Don't need to process this frame again builder->mNewDisplayItemData.RawRemoveEntry(newDisplayItems); return PL_DHASH_NEXT; @@ -672,17 +649,12 @@ FrameLayerBuilder::StoreNewDisplayItemData(DisplayItemDataEntry* aEntry, // Remember that this frame has display items in retained layers NS_ASSERTION(!data->mFramesWithLayers.GetEntry(f), "We shouldn't get here if we're already in mFramesWithLayers"); - data->mFramesWithLayers.PutEntry(f); - NS_ASSERTION(!props.Get(DisplayItemDataProperty()), + DisplayItemDataEntry *newEntry = data->mFramesWithLayers.PutEntry(f); + NS_ASSERTION(!props.Get(LayerManagerDataProperty()), "mFramesWithLayers out of sync"); - void* propValue; - nsTArray* array = - new (&propValue) nsTArray(); - // Steal the list of display item layers - array->SwapElements(aEntry->mData); - // Save it - props.Set(DisplayItemDataProperty(), propValue); + newEntry->mData.SwapElements(aEntry->mData); + props.Set(LayerManagerDataProperty(), data); if (f->GetStateBits() & NS_FRAME_HAS_CONTAINER_LAYER) { props.Set(ThebesLayerInvalidRegionProperty(), new nsRegion()); @@ -693,12 +665,10 @@ FrameLayerBuilder::StoreNewDisplayItemData(DisplayItemDataEntry* aEntry, bool FrameLayerBuilder::HasRetainedLayerFor(nsIFrame* aFrame, PRUint32 aDisplayItemKey) { - void* propValue = aFrame->Properties().Get(DisplayItemDataProperty()); - if (!propValue) + nsTArray *array = GetDisplayItemDataArrayForFrame(aFrame); + if (!array) return PR_FALSE; - nsTArray* array = - (reinterpret_cast*>(&propValue)); for (PRUint32 i = 0; i < array->Length(); ++i) { if (array->ElementAt(i).mDisplayItemKey == aDisplayItemKey) { Layer* layer = array->ElementAt(i).mLayer; @@ -719,12 +689,10 @@ FrameLayerBuilder::GetOldLayerFor(nsIFrame* aFrame, PRUint32 aDisplayItemKey) if (!mRetainingManager || mInvalidateAllLayers) return nsnull; - void* propValue = aFrame->Properties().Get(DisplayItemDataProperty()); - if (!propValue) + nsTArray *array = GetDisplayItemDataArrayForFrame(aFrame); + if (!array) return nsnull; - nsTArray* array = - (reinterpret_cast*>(&propValue)); for (PRUint32 i = 0; i < array->Length(); ++i) { if (array->ElementAt(i).mDisplayItemKey == aDisplayItemKey) { Layer* layer = array->ElementAt(i).mLayer; @@ -1954,12 +1922,10 @@ FrameLayerBuilder::InvalidateAllLayers(LayerManager* aManager) Layer* FrameLayerBuilder::GetDedicatedLayer(nsIFrame* aFrame, PRUint32 aDisplayItemKey) { - void* propValue = aFrame->Properties().Get(DisplayItemDataProperty()); - if (!propValue) + nsTArray* array = GetDisplayItemDataArrayForFrame(aFrame); + if (!array) return nsnull; - nsTArray* array = - (reinterpret_cast*>(&propValue)); for (PRUint32 i = 0; i < array->Length(); ++i) { if (array->ElementAt(i).mDisplayItemKey == aDisplayItemKey) { Layer* layer = array->ElementAt(i).mLayer; diff --git a/layout/base/FrameLayerBuilder.h b/layout/base/FrameLayerBuilder.h index e4b745bb1202..3c55deb55477 100644 --- a/layout/base/FrameLayerBuilder.h +++ b/layout/base/FrameLayerBuilder.h @@ -279,18 +279,6 @@ public: */ Layer* GetOldLayerFor(nsIFrame* aFrame, PRUint32 aDisplayItemKey); - /** - * A useful hashtable iteration function that removes the - * DisplayItemData property for the frame, clears its - * NS_FRAME_HAS_CONTAINER_LAYER bit and returns PL_DHASH_REMOVE. - * aClosure is ignored. - */ - static PLDHashOperator RemoveDisplayItemDataForFrame(nsPtrHashKey* aEntry, - void* aClosure) - { - return UpdateDisplayItemDataForFrame(aEntry, nsnull); - } - /** * Try to determine whether the ThebesLayer aLayer paints an opaque * single color everywhere it's visible in aRect. @@ -300,11 +288,12 @@ public: ThebesLayer* aLayer, const nsRect& aRect); /** - * Destroy any stored DisplayItemDataProperty for aFrame. + * Destroy any stored LayerManagerDataProperty and the associated data for + * aFrame. */ static void DestroyDisplayItemDataFor(nsIFrame* aFrame) { - aFrame->Properties().Delete(DisplayItemDataProperty()); + aFrame->Properties().Delete(LayerManagerDataProperty()); } LayerManager* GetRetainingLayerManager() { return mRetainingManager; } @@ -421,18 +410,10 @@ protected: LayerState mLayerState; }; - static void InternalDestroyDisplayItemData(nsIFrame* aFrame, - void* aPropertyValue, - bool aRemoveFromFramesWithLayers); - static void DestroyDisplayItemData(nsIFrame* aFrame, void* aPropertyValue); + static void RemoveFrameFromLayerManager(nsIFrame* aFrame, void* aPropertyValue); - /** - * For DisplayItemDataProperty, the property value *is* an - * nsTArray, not a pointer to an array. This works - * because sizeof(nsTArray) == sizeof(void*). - */ - NS_DECLARE_FRAME_PROPERTY_WITH_FRAME_IN_DTOR(DisplayItemDataProperty, - DestroyDisplayItemData) + NS_DECLARE_FRAME_PROPERTY_WITH_FRAME_IN_DTOR(LayerManagerDataProperty, + RemoveFrameFromLayerManager) /** * We accumulate DisplayItemData elements in a hashtable during @@ -442,19 +423,45 @@ protected: class DisplayItemDataEntry : public nsPtrHashKey { public: DisplayItemDataEntry(const nsIFrame *key) : nsPtrHashKey(key) {} - DisplayItemDataEntry(const DisplayItemDataEntry &toCopy) : - nsPtrHashKey(toCopy.mKey), mData(toCopy.mData) + DisplayItemDataEntry(DisplayItemDataEntry &toCopy) : + nsPtrHashKey(toCopy.mKey) { - NS_ERROR("Should never be called, since we ALLOW_MEMMOVE"); + // This isn't actually a copy-constructor; notice that it steals toCopy's + // array. Be careful. + mData.SwapElements(toCopy.mData); } bool HasNonEmptyContainerLayer(); - nsTArray mData; + nsAutoTArray mData; - enum { ALLOW_MEMMOVE = PR_TRUE }; + enum { ALLOW_MEMMOVE = false }; }; + // LayerManagerData needs to see DisplayItemDataEntry. + friend class LayerManagerData; + + /* + * Get the DisplayItemData array associated with this frame, or null if one + * doesn't exist. + * + * Note that the pointer returned here is only valid so long as you don't + * poke the LayerManagerData's mFramesWithLayers hashtable. + */ + static nsTArray* GetDisplayItemDataArrayForFrame(nsIFrame *aFrame); + + /** + * A useful hashtable iteration function that removes the + * DisplayItemData property for the frame, clears its + * NS_FRAME_HAS_CONTAINER_LAYER bit and returns PL_DHASH_REMOVE. + * aClosure is ignored. + */ + static PLDHashOperator RemoveDisplayItemDataForFrame(DisplayItemDataEntry* aEntry, + void* aClosure) + { + return UpdateDisplayItemDataForFrame(aEntry, nsnull); + } + /** * We store one of these for each display item associated with a * ThebesLayer, in a hashtable that maps each ThebesLayer to an array @@ -503,7 +510,7 @@ protected: void RemoveThebesItemsForLayerSubtree(Layer* aLayer); - static PLDHashOperator UpdateDisplayItemDataForFrame(nsPtrHashKey* aEntry, + static PLDHashOperator UpdateDisplayItemDataForFrame(DisplayItemDataEntry* aEntry, void* aUserArg); static PLDHashOperator StoreNewDisplayItemData(DisplayItemDataEntry* aEntry, void* aUserArg); diff --git a/layout/base/crashtests/crashtests.list b/layout/base/crashtests/crashtests.list index 4a0598bb63e7..ff1ff98f7161 100644 --- a/layout/base/crashtests/crashtests.list +++ b/layout/base/crashtests/crashtests.list @@ -107,7 +107,7 @@ asserts-if(browserIsRemote,1) load 347898-1.html # bug 622188 load 348126-1.html load 348688-1.html load 348708-1.xhtml -asserts(4) load 348729-1.html # bug 548836 +asserts(2) load 348729-1.html # bug 548836 load 349095-1.xhtml load 350128-1.xhtml load 350267-1.html diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 55ec4803360b..810caa79b863 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -680,6 +680,43 @@ GetMouseThrough(const nsIFrame* aFrame) return PR_FALSE; } +// A list of frames, and their z depth. Used for sorting +// the results of hit testing. +struct FramesWithDepth +{ + FramesWithDepth(float aDepth) : + mDepth(aDepth) + {} + + bool operator<(const FramesWithDepth& aOther) const { + if (mDepth != aOther.mDepth) { + // We want to sort so that the shallowest item (highest depth value) is first + return mDepth > aOther.mDepth; + } + return this < &aOther; + } + bool operator==(const FramesWithDepth& aOther) const { + return this == &aOther; + } + + float mDepth; + nsTArray mFrames; +}; + +// Sort the frames by depth and then moves all the contained frames to the destination +void FlushFramesArray(nsTArray& aSource, nsTArray* aDest) +{ + if (aSource.IsEmpty()) { + return; + } + aSource.Sort(); + PRUint32 length = aSource.Length(); + for (PRUint32 i = 0; i < length; i++) { + aDest->MoveElementsFrom(aSource[i].mFrames); + } + aSource.Clear(); +} + void nsDisplayList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, nsDisplayItem::HitTestState* aState, nsTArray *aOutFrames) const { @@ -688,6 +725,7 @@ void nsDisplayList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, for (item = GetBottom(); item; item = item->GetAbove()) { aState->mItemBuffer.AppendElement(item); } + nsAutoTArray temp; for (PRInt32 i = aState->mItemBuffer.Length() - 1; i >= itemBufferStart; --i) { // Pop element off the end of the buffer. We want to shorten the buffer // so that recursive calls to HitTest have more buffer space. @@ -697,18 +735,38 @@ void nsDisplayList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, if (aRect.Intersects(item->GetBounds(aBuilder))) { nsAutoTArray outFrames; item->HitTest(aBuilder, aRect, aState, &outFrames); + + // For 3d transforms with preserve-3d we add hit frames into the temp list + // so we can sort them later, otherwise we add them directly to the output list. + nsTArray *writeFrames = aOutFrames; + if (item->GetType() == nsDisplayItem::TYPE_TRANSFORM && + item->GetUnderlyingFrame()->Preserves3D()) { + nsDisplayTransform *transform = static_cast(item); + nsPoint point = aRect.TopLeft(); + // A 1x1 rect means a point, otherwise use the center of the rect + if (aRect.width != 1 || aRect.height != 1) { + point = aRect.Center(); + } + temp.AppendElement(FramesWithDepth(transform->GetHitDepthAtPoint(point))); + writeFrames = &temp[temp.Length() - 1].mFrames; + } else { + // We may have just finished a run of consecutive preserve-3d transforms, + // so flush these into the destination array before processing our frame list. + FlushFramesArray(temp, aOutFrames); + } for (PRUint32 j = 0; j < outFrames.Length(); j++) { nsIFrame *f = outFrames.ElementAt(j); // Handle the XUL 'mousethrough' feature and 'pointer-events'. if (!GetMouseThrough(f) && f->GetStyleVisibility()->mPointerEvents != NS_STYLE_POINTER_EVENTS_NONE) { - aOutFrames->AppendElement(f); + writeFrames->AppendElement(f); } } - } } + // Clear any remaining preserve-3d transforms. + FlushFramesArray(temp, aOutFrames); NS_ASSERTION(aState->mItemBuffer.Length() == PRUint32(itemBufferStart), "How did we forget to pop some elements?"); } @@ -762,24 +820,6 @@ static bool IsContentLEQ(nsDisplayItem* aItem1, nsDisplayItem* aItem2, static_cast(aClosure)) <= 0; } -static bool IsZPositionLEQ(nsDisplayItem* aItem1, nsDisplayItem* aItem2, - void* aClosure) { - if (!aItem1->GetUnderlyingFrame()->Preserves3D() || - !aItem1->GetUnderlyingFrame()->Preserves3D()) { - return IsContentLEQ(aItem1, aItem2, aClosure); - } - - nsIFrame* ancestor; - gfx3DMatrix matrix1 = aItem1->GetUnderlyingFrame()->GetTransformMatrix(&ancestor); - gfx3DMatrix matrix2 = aItem2->GetUnderlyingFrame()->GetTransformMatrix(&ancestor); - - if (matrix1._43 == matrix2._43) { - return IsContentLEQ(aItem1, aItem2, aClosure); - } - - return matrix1._43 < matrix2._43; -} - static bool IsZOrderLEQ(nsDisplayItem* aItem1, nsDisplayItem* aItem2, void* aClosure) { // These GetUnderlyingFrame calls return non-null because we're only used @@ -788,7 +828,7 @@ static bool IsZOrderLEQ(nsDisplayItem* aItem1, nsDisplayItem* aItem2, PRInt32 index1 = nsLayoutUtils::GetZIndex(aItem1->GetUnderlyingFrame()); PRInt32 index2 = nsLayoutUtils::GetZIndex(aItem2->GetUnderlyingFrame()); if (index1 == index2) - return IsZPositionLEQ(aItem1, aItem2, aClosure); + return IsContentLEQ(aItem1, aItem2, aClosure); return index1 < index2; } @@ -835,11 +875,6 @@ void nsDisplayList::SortByContentOrder(nsDisplayListBuilder* aBuilder, Sort(aBuilder, IsContentLEQ, aCommonAncestor); } -void nsDisplayList::SortByZPosition(nsDisplayListBuilder* aBuilder, - nsIContent* aCommonAncestor) { - Sort(aBuilder, IsZPositionLEQ, aCommonAncestor); -} - void nsDisplayList::Sort(nsDisplayListBuilder* aBuilder, SortLEQ aCmp, void* aClosure) { ExplodeAnonymousChildLists(aBuilder); @@ -2536,9 +2571,16 @@ already_AddRefed nsDisplayTransform::BuildLayer(nsDisplayListBuilder *aBu return nsnull; } - return aBuilder->LayerBuilder()-> + nsRefPtr container = aBuilder->LayerBuilder()-> BuildContainerLayerFor(aBuilder, aManager, mFrame, this, *mStoredList.GetList(), aContainerParameters, &newTransformMatrix); + + // Add the preserve-3d flag for this layer, BuildContainerLayerFor clears all flags, + // so we never need to explicitely unset this flag. + if (mFrame->Preserves3D()) { + container->SetContentFlags(container->GetContentFlags() | Layer::CONTENT_PRESERVE_3D); + } + return container.forget(); } nsDisplayItem::LayerState @@ -2546,7 +2588,7 @@ nsDisplayTransform::GetLayerState(nsDisplayListBuilder* aBuilder, LayerManager* aManager) { if (mFrame->AreLayersMarkedActive(nsChangeHint_UpdateTransformLayer)) return LAYER_ACTIVE; - if (!GetTransform(mFrame->PresContext()->AppUnitsPerDevPixel()).Is2D()) + if (!GetTransform(mFrame->PresContext()->AppUnitsPerDevPixel()).Is2D() || mFrame->Preserves3D()) return LAYER_ACTIVE; nsIFrame* activeScrolledRoot = nsLayoutUtils::GetActiveScrolledRootFor(mFrame, nsnull); @@ -2658,6 +2700,24 @@ void nsDisplayTransform::HitTest(nsDisplayListBuilder *aBuilder, } +float +nsDisplayTransform::GetHitDepthAtPoint(const nsPoint& aPoint) +{ + float factor = nsPresContext::AppUnitsPerCSSPixel(); + gfx3DMatrix matrix = GetTransform(factor); + + NS_ASSERTION(!matrix.IsSingular(), "We can't have hit a singular matrix!"); + NS_ASSERTION(mFrame->GetStyleDisplay()->mBackfaceVisibility != NS_STYLE_BACKFACE_VISIBILITY_HIDDEN || + matrix.GetNormalVector().z > 0.0, "We can't have hit the hidden backface of a layer!"); + + gfxPoint point = + matrix.Inverse().ProjectPoint(gfxPoint(NSAppUnitsToFloatPixels(aPoint.x, factor), + NSAppUnitsToFloatPixels(aPoint.y, factor))); + + gfxPoint3D transformed = matrix.Transform3D(gfxPoint3D(point.x, point.y, 0)); + return transformed.z; +} + /* The bounding rectangle for the object is the overflow rectangle translated * by the reference point. */ diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index 91a29b667f7d..fbd526c06906 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -1012,8 +1012,6 @@ public: */ void SortByContentOrder(nsDisplayListBuilder* aBuilder, nsIContent* aCommonAncestor); - void SortByZPosition(nsDisplayListBuilder* aBuilder, nsIContent* aCommonAncestor); - /** * Generic stable sort. Take care, because some of the items might be nsDisplayLists * themselves. @@ -2136,6 +2134,8 @@ public: const gfx3DMatrix& GetTransform(float aFactor); + float GetHitDepthAtPoint(const nsPoint& aPoint); + /** * TransformRect takes in as parameters a rectangle (in aFrame's coordinate * space) and returns the smallest rectangle (in aFrame's coordinate space) diff --git a/layout/base/tests/Makefile.in b/layout/base/tests/Makefile.in index e37414fa8c68..d241d692af3b 100644 --- a/layout/base/tests/Makefile.in +++ b/layout/base/tests/Makefile.in @@ -63,6 +63,8 @@ DEFINES += -D_IMPL_NS_LAYOUT _TEST_FILES = \ border_radius_hit_testing_iframe.html \ + test_preserve3d_sorting_hit_testing.html \ + preserve3d_sorting_hit_testing_iframe.html \ bug369950-subframe.xml \ decoration_line_rendering.js \ test_after_paint_pref.html \ diff --git a/layout/base/tests/preserve3d_sorting_hit_testing_iframe.html b/layout/base/tests/preserve3d_sorting_hit_testing_iframe.html new file mode 100644 index 000000000000..4c886930c61f --- /dev/null +++ b/layout/base/tests/preserve3d_sorting_hit_testing_iframe.html @@ -0,0 +1,32 @@ + +preserve-3d hit testing + + +
+
+
+
+
+
diff --git a/layout/base/tests/test_preserve3d_sorting_hit_testing.html b/layout/base/tests/test_preserve3d_sorting_hit_testing.html new file mode 100644 index 000000000000..7ced6c9da6fb --- /dev/null +++ b/layout/base/tests/test_preserve3d_sorting_hit_testing.html @@ -0,0 +1,48 @@ + + + + + Test for Bug 684759 + + + + +Mozilla Bug 684759 + +
+
+
+ + diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 9479c591aad5..e024103d60a5 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -1549,7 +1549,8 @@ WrapPreserve3DListInternal(nsIFrame* aFrame, nsDisplayListBuilder *aBuilder, nsD } nsDisplayWrapList *list = static_cast(item); rv = WrapPreserve3DListInternal(aFrame, aBuilder, list->GetList(), aIndex); - newList.AppendToTop(item); + newList.AppendToTop(list->GetList()); + list->~nsDisplayWrapList(); break; } case nsDisplayItem::TYPE_OPACITY: { @@ -1774,13 +1775,6 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, rv = WrapPreserve3DList(this, aBuilder, &resultList); if (NS_FAILED(rv)) return rv; - - if (resultList.Count() > 1) { - rv = resultList.AppendNewToTop( - new (aBuilder) nsDisplayWrapList(aBuilder, this, &resultList)); - if (NS_FAILED(rv)) - return rv; - } } else { rv = resultList.AppendNewToTop( new (aBuilder) nsDisplayTransform(aBuilder, this, &resultList)); diff --git a/layout/generic/test/file_bug514732_window.xul b/layout/generic/test/file_bug514732_window.xul index 37d5e464b982..d8cad98de3fc 100755 --- a/layout/generic/test/file_bug514732_window.xul +++ b/layout/generic/test/file_bug514732_window.xul @@ -8,6 +8,12 @@ onload="setTimeout(nextTest,0);" title="bug 514732 test"> + diff --git a/layout/reftests/abs-pos/multi-column-1-ref.html b/layout/reftests/abs-pos/multi-column-1-ref.html new file mode 100644 index 000000000000..09ca0eaf198b --- /dev/null +++ b/layout/reftests/abs-pos/multi-column-1-ref.html @@ -0,0 +1,29 @@ + + + + + + +
+

Test

+

Test

+

XXX
XXXX
XXX
XXXX
XXX

+
+ + diff --git a/layout/reftests/abs-pos/multi-column-1.html b/layout/reftests/abs-pos/multi-column-1.html new file mode 100644 index 000000000000..07592668c7dc --- /dev/null +++ b/layout/reftests/abs-pos/multi-column-1.html @@ -0,0 +1,31 @@ + + + + + + +
+

Test

+

Test

+

XXX
XXXX
XXX
XXXX
XXX
XXXX
XXX
XXXX
XXX
XXXX

+
+ + diff --git a/layout/reftests/abs-pos/reftest.list b/layout/reftests/abs-pos/reftest.list index eaa3c08c177f..22ed91e6bb49 100644 --- a/layout/reftests/abs-pos/reftest.list +++ b/layout/reftests/abs-pos/reftest.list @@ -13,3 +13,4 @@ == select-1-dynamic.html select-1-ref.html == select-2.html select-2-ref.html == select-3.html select-3-ref.html +== multi-column-1.html multi-column-1-ref.html diff --git a/layout/reftests/svg/image/imported-image-01.svg b/layout/reftests/svg/image/imported-image-01.svg new file mode 100644 index 000000000000..b7ef5d5acf23 --- /dev/null +++ b/layout/reftests/svg/image/imported-image-01.svg @@ -0,0 +1,12 @@ + + + + + diff --git a/layout/reftests/svg/image/imported-image-02.svg b/layout/reftests/svg/image/imported-image-02.svg new file mode 100644 index 000000000000..7e510257952c --- /dev/null +++ b/layout/reftests/svg/image/imported-image-02.svg @@ -0,0 +1,17 @@ + + + + + diff --git a/layout/reftests/svg/image/imported-image-ref.svg b/layout/reftests/svg/image/imported-image-ref.svg new file mode 100644 index 000000000000..4b0d1accc9f0 --- /dev/null +++ b/layout/reftests/svg/image/imported-image-ref.svg @@ -0,0 +1,6 @@ + + + + diff --git a/layout/reftests/svg/image/reftest.list b/layout/reftests/svg/image/reftest.list index afb9e53d1f5b..6f6d9e2af972 100644 --- a/layout/reftests/svg/image/reftest.list +++ b/layout/reftests/svg/image/reftest.list @@ -17,6 +17,8 @@ == image-zoom-01a.svg image-zoom-01-ref.svg == image-zoom-01b.svg image-zoom-01-ref.svg == image-zoom-02.svg image-zoom-02-ref.svg +== imported-image-01.svg imported-image-ref.svg +== imported-image-02.svg imported-image-ref.svg # Tests for with preserveAspectRatio == image-preserveAspectRatio-01-raster.svg image-preserveAspectRatio-01-ref.svg diff --git a/layout/reftests/transform-3d/reftest.list b/layout/reftests/transform-3d/reftest.list index a5fb50d91dab..6992a9095e67 100644 --- a/layout/reftests/transform-3d/reftest.list +++ b/layout/reftests/transform-3d/reftest.list @@ -26,3 +26,8 @@ fails == preserve3d-1a.html preserve3d-1-ref.html == backface-visibility-1b.html about:blank != perspective-origin-1a.html rotatex-perspective-1a.html == perspective-origin-1b.html perspective-origin-1a.html +!= sorting-1a.html sorting-1-ref.html +# Parallel planes, different z depth +== sorting-2a.html sorting-2-ref.html +# Parallel planes, same z depth (shouldn't be sorted!) +== sorting-2b.html sorting-2-ref.html diff --git a/layout/reftests/transform-3d/sorting-1-ref.html b/layout/reftests/transform-3d/sorting-1-ref.html new file mode 100644 index 000000000000..340d2eb0bb90 --- /dev/null +++ b/layout/reftests/transform-3d/sorting-1-ref.html @@ -0,0 +1,35 @@ + + + + + +
+
+
+
+
+
+ + diff --git a/layout/reftests/transform-3d/sorting-1a.html b/layout/reftests/transform-3d/sorting-1a.html new file mode 100644 index 000000000000..98414f843b7a --- /dev/null +++ b/layout/reftests/transform-3d/sorting-1a.html @@ -0,0 +1,35 @@ + + + + + +
+
+
+
+
+
+ + diff --git a/layout/reftests/transform-3d/sorting-2-ref.html b/layout/reftests/transform-3d/sorting-2-ref.html new file mode 100644 index 000000000000..cb34eaf4ed3d --- /dev/null +++ b/layout/reftests/transform-3d/sorting-2-ref.html @@ -0,0 +1,21 @@ + + + + + +
+
+ + diff --git a/layout/reftests/transform-3d/sorting-2a.html b/layout/reftests/transform-3d/sorting-2a.html new file mode 100644 index 000000000000..988ba323c244 --- /dev/null +++ b/layout/reftests/transform-3d/sorting-2a.html @@ -0,0 +1,29 @@ + + + + + +
+
+
+
+ + diff --git a/layout/reftests/transform-3d/sorting-2b.html b/layout/reftests/transform-3d/sorting-2b.html new file mode 100644 index 000000000000..5828be845edf --- /dev/null +++ b/layout/reftests/transform-3d/sorting-2b.html @@ -0,0 +1,30 @@ + + + + + +
+
+
+
+ + diff --git a/layout/style/test/test_bug389464.html b/layout/style/test/test_bug389464.html index 8e22f2a326f5..0d9b661c958b 100644 --- a/layout/style/test/test_bug389464.html +++ b/layout/style/test/test_bug389464.html @@ -1,6 +1,7 @@ @@ -27,24 +28,10 @@ SimpleTest.waitForExplicitFinish(); -function get_pref(pref) -{ - return SpecialPowers.getIntPref("font.size." + pref); -} - -function set_pref(pref, val) -{ - SpecialPowers.setIntPref("font.size." + pref, val); -} - var cs1 = getComputedStyle(document.getElementById("one"), ""); var cs2 = getComputedStyle(document.getElementById("two"), ""); -var oldVariable = get_pref("variable.x-western"); -var oldFixed = get_pref("fixed.x-western"); -set_pref("variable.x-western", 25); -set_pref("fixed.x-western", 20); -setTimeout(part1, 0); +SpecialPowers.pushPrefEnv({'set': [['variable.x-western', 25], ['fixed.x-western', 20]]}, function() setTimeout(part1, 0)); function part1() { @@ -52,8 +39,6 @@ function part1() var fs2 = cs2.fontSize.match(/(.*)px/)[1]; ok(fs1 < fs2, " shrinks relative to font-family: -moz-fixed"); - set_pref("variable.x-western", oldVariable); - set_pref("fixed.x-western", oldFixed); SimpleTest.finish(); } diff --git a/layout/style/test/test_bug401046.html b/layout/style/test/test_bug401046.html index a97b08e9b126..6af4d00192ec 100644 --- a/layout/style/test/test_bug401046.html +++ b/layout/style/test/test_bug401046.html @@ -56,10 +56,7 @@ function fs(idx) { return getComputedStyle(elts[idx], "").marginBottom; } -SpecialPowers.clearUserPref('font.minimum-size.x-western'); - -// preference change is async (although one setTimeout might be enough?) -setTimeout(setTimeout, 0, step1, 0); +SpecialPowers.pushPrefEnv({'clear': [['font.minimum-size.x-western']]}, function() setTimeout(step1, 0)); function step1() { is(fs(0), "0px", "at min font size 0, 0px should compute to 0px"); @@ -67,10 +64,7 @@ function step1() { is(fs(2), "12px", "at min font size 0, 12px should compute to 12px"); is(fs(3), "28px", "at min font size 0, 28px should compute to 28px"); - SpecialPowers.setIntPref('font.minimum-size.x-western', 7); - - // preference change is async (although one setTimeout might be enough?) - setTimeout(setTimeout, 0, step2, 0); + SpecialPowers.pushPrefEnv({'set': [['font.minimum-size.x-western', 7]]}, function() setTimeout(step2, 0)); } function step2() { @@ -79,10 +73,7 @@ function step2() { is(fs(2), "12px", "at min font size 7, 12px should compute to 12px"); is(fs(3), "28px", "at min font size 7, 28px should compute to 28px"); - SpecialPowers.setIntPref('font.minimum-size.x-western', 18); - - // preference change is async (although one setTimeout might be enough?) - setTimeout(setTimeout, 0, step3, 0); + SpecialPowers.pushPrefEnv({'set': [['font.minimum-size.x-western', 18]]}, function() setTimeout(step3, 0)); } function step3() { @@ -91,9 +82,7 @@ function step3() { is(fs(2), "18px", "at min font size 18, 12px should compute to 18px"); is(fs(3), "28px", "at min font size 18, 28px should compute to 28px"); - SpecialPowers.clearUserPref('font.minimum-size.x-western'); - - SimpleTest.finish(); + SpecialPowers.pushPrefEnv({'clear': [['font.minimum-size.x-western']]}, SimpleTest.finish); } diff --git a/layout/style/test/test_pointer-events.html b/layout/style/test/test_pointer-events.html index a90e3c3a8ec9..c274270553a5 100644 --- a/layout/style/test/test_pointer-events.html +++ b/layout/style/test/test_pointer-events.html @@ -4,7 +4,7 @@ Test for pointer-events in HTML - +