author | Ryan VanderMeulen <ryanvm@gmail.com> |
Tue, 18 Jun 2013 19:04:48 -0400 | |
changeset 135493 | d2a7cfa34154439232ac1d6230b3945b32fe4d5e |
parent 135440 | 2e810d6c8779a1908c5d6785d00954a0e5018c58 (current diff) |
parent 135492 | dd9245e218125a081c46d35dede11d13f704866f (diff) |
child 135535 | a8a28bd8abf391bec27a465597d247e78639ed76 |
child 135574 | 568921d73dd121b23b7c60e6a5b34daa703a0bd4 |
child 135593 | 531048ab5fedcfe21629e2ac761dd2a353a0cfb9 |
push id | 24841 |
push user | ryanvm@gmail.com |
push date | Tue, 18 Jun 2013 23:04:53 +0000 |
treeherder | mozilla-central@d2a7cfa34154 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 24.0a1 |
first release with | nightly linux32
d2a7cfa34154
/
24.0a1
/
20130619031048
/
files
nightly linux64
d2a7cfa34154
/
24.0a1
/
20130619031048
/
files
nightly mac
d2a7cfa34154
/
24.0a1
/
20130619031048
/
files
nightly win32
d2a7cfa34154
/
24.0a1
/
20130619031048
/
files
nightly win64
d2a7cfa34154
/
24.0a1
/
20130619031048
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
24.0a1
/
20130619031048
/
pushlog to previous
nightly linux64
24.0a1
/
20130619031048
/
pushlog to previous
nightly mac
24.0a1
/
20130619031048
/
pushlog to previous
nightly win32
24.0a1
/
20130619031048
/
pushlog to previous
nightly win64
24.0a1
/
20130619031048
/
pushlog to previous
|
layout/reftests/svg/non-scaling-stroke-03-ref.svg | file | annotate | diff | comparison | revisions | |
layout/reftests/svg/non-scaling-stroke-03.svg | file | annotate | diff | comparison | revisions |
--- a/addon-sdk/source/test/tabs/test-firefox-tabs.js +++ b/addon-sdk/source/test/tabs/test-firefox-tabs.js @@ -1145,8 +1145,12 @@ function openBrowserWindow(callback, url // Helper for calling code at window close function closeBrowserWindow(window, callback) { window.addEventListener("unload", function unload() { window.removeEventListener("unload", unload, false); callback(); }, false); window.close(); } + +// Test disabled on Linux because of bug 882867 +if (require("sdk/system/runtime").OS == "Linux") + module.exports = {};
--- a/browser/components/search/content/search.xml +++ b/browser/components/search/content/search.xml @@ -483,27 +483,32 @@ openUILinkIn(submission.uri.spec, aWhere, null, submission.postData); ]]></body> </method> </implementation> <handlers> <handler event="command"><![CDATA[ const target = event.originalTarget; - if (target.classList.contains("addengine-item")) { + if (target.engine) { + this.currentEngine = target.engine; + } else if (target.classList.contains("addengine-item")) { var searchService = Components.classes["@mozilla.org/browser/search-service;1"] .getService(Components.interfaces.nsIBrowserSearchService); // We only detect OpenSearch files var type = Components.interfaces.nsISearchEngine.DATA_XML; + // Select the installed engine if the installation succeeds + var installCallback = { + onSuccess: engine => this.currentEngine = engine + } searchService.addEngine(target.getAttribute("uri"), type, - target.getAttribute("src"), false); + target.getAttribute("src"), false, + installCallback); } - else if (target.engine) - this.currentEngine = target.engine; else return; this.focus(); this.select(); ]]></handler> <handler event="popupshowing" action="this.rebuildPopupDynamic();"/>
--- a/browser/components/search/test/browser_426329.js +++ b/browser/components/search/test/browser_426329.js @@ -25,18 +25,17 @@ function test() { let testIterator; function observer(aSub, aTopic, aData) { switch (aData) { case "engine-added": var engine = ss.getEngineByName("Bug 426329"); ok(engine, "Engine was added."); - //XXX Bug 493051 - //ss.currentEngine = engine; + ss.currentEngine = engine; break; case "engine-current": ok(ss.currentEngine.name == "Bug 426329", "currentEngine set"); testReturn(); break; case "engine-removed": Services.obs.removeObserver(observer, "browser-search-engine-modified"); finish();
--- a/browser/components/search/test/browser_contextmenu.js +++ b/browser/components/search/test/browser_contextmenu.js @@ -11,18 +11,17 @@ function test() { const ENGINE_NAME = "Foo"; var contextMenu; function observer(aSub, aTopic, aData) { switch (aData) { case "engine-added": var engine = ss.getEngineByName(ENGINE_NAME); ok(engine, "Engine was added."); - //XXX Bug 493051 - //ss.currentEngine = engine; + ss.currentEngine = engine; break; case "engine-current": is(ss.currentEngine.name, ENGINE_NAME, "currentEngine set"); startTest(); break; case "engine-removed": Services.obs.removeObserver(observer, "browser-search-engine-modified"); finish();
--- a/browser/components/search/test/browser_private_search_perwindowpb.js +++ b/browser/components/search/test/browser_private_search_perwindowpb.js @@ -37,30 +37,28 @@ function test() { onPageLoad(aWin, aCallback); searchBar.value = aIsPrivate ? "private test" : "public test"; searchBar.focus(); EventUtils.synthesizeKey("VK_RETURN", {}, aWin); } function addEngine(aCallback) { - function observer(aSub, aTopic, aData) { - switch (aData) { - case "engine-current": - ok(Services.search.currentEngine.name == "Bug 426329", - "currentEngine set"); - aCallback(); - break; + let installCallback = { + onSuccess: function (engine) { + Services.search.currentEngine = engine; + aCallback(); + }, + onError: function (errorCode) { + ok(false, "failed to install engine: " + errorCode); } - } - - Services.obs.addObserver(observer, "browser-search-engine-modified", false); - Services.search.addEngine( - engineURL + "426329.xml", Ci.nsISearchEngine.DATA_XML, - "data:image/x-icon,%00", false); + }; + Services.search.addEngine(engineURL + "426329.xml", + Ci.nsISearchEngine.DATA_XML, + "data:image/x-icon,%00", false, installCallback); } function testOnWindow(aIsPrivate, aCallback) { let win = OpenBrowserWindow({ private: aIsPrivate }); waitForFocus(function() { windowsToClose.push(win); executeSoon(function() aCallback(win)); }, win);
--- a/browser/metro/base/content/helperui/SelectionHelperUI.js +++ b/browser/metro/base/content/helperui/SelectionHelperUI.js @@ -827,45 +827,35 @@ var SelectionHelperUI = { // shutdown but leave focus alone. the event will fall through // and the dom will update focus for us. If the user tapped on // another input, we'll get a attachToCaret call soonish on the // new input. this.closeEditSession(false); return; } - let selectionTap = this._hitTestSelection(aEvent); - - // If the tap is in the selection, just ignore it. We disallow this - // since we always get a single tap before a double, and double tap - // copies selected text. - if (selectionTap) { - if (!this._targetIsEditable) { - this.closeEditSession(false); - return; - } + if (this._hitTestSelection(aEvent) && this._targetIsEditable) { // Attach to the newly placed caret position this._sendAsyncMessage("Browser:CaretAttach", { xPos: aEvent.clientX, yPos: aEvent.clientY }); return; } // A tap within an editable but outside active selection, clear the // selection and flip back to caret mode. if (this.startMark.visible && pointInTargetElement && this._targetIsEditable) { this._transitionFromSelectionToCaret(clientCoords.x, clientCoords.y); + return; } - // If we have active selection in anything else don't let the event get - // to content. Prevents random taps from killing active selection. - aEvent.stopPropagation(); - aEvent.preventDefault(); + // Close when we get a single tap in content. + this.closeEditSession(false); }, _onKeypress: function _onKeypress() { this.closeEditSession(); }, _onResize: function _onResize() { this._sendAsyncMessage("Browser:SelectionUpdate", {});
--- a/browser/metro/base/tests/mochitest/browser_selection_basic.js +++ b/browser/metro/base/tests/mochitest/browser_selection_basic.js @@ -301,125 +301,56 @@ gTests.push({ clearSelection(gFrame); yield waitForCondition(function () { return !SelectionHelperUI.isSelectionUIVisible; }, kCommonWaitMs, kCommonPollMs); yield hideContextUI(); }, }); -/* -disable until bug 860248 is addressed. gTests.push({ - desc: "double-tap copy text in content", - setUp: setUpHelper, + desc: "tap on selection clears selection in content", + setUp: setUpAndTearDown, run: function test() { sendContextMenuClick(30, 20); yield waitForCondition(function () { return SelectionHelperUI.isSelectionUIVisible; }, kCommonWaitMs, kCommonPollMs); - sendDoubleTap(gWindow, 30, 20); + sendTap(gWindow, 30, 20); yield waitForCondition(function () { return !SelectionHelperUI.isSelectionUIVisible; }, kCommonWaitMs, kCommonPollMs); - - // check copy text results - let text = SpecialPowers.getClipboardData("text/unicode").trim(); - is(text, "There", "copy text test"); - - // check for active selection - is(getTrimmedSelection(gWindow).toString(), "", "selection test"); }, - tearDown: tearDownHelper, + tearDown: setUpAndTearDown, }); gTests.push({ - desc: "double-tap copy text in scrolled content", - setUp: setUpHelper, + desc: "tap off selection clears selection in content", + setUp: setUpAndTearDown, run: function test() { - let scrollPromise = waitForEvent(gWindow, "scroll"); - gWindow.scrollBy(0, 200); - yield scrollPromise; - ok(scrollPromise && !(scrollPromise instanceof Error), "scrollPromise error"); - sendContextMenuClick(30, 100); + sendContextMenuClick(30, 20); yield waitForCondition(function () { return SelectionHelperUI.isSelectionUIVisible; }, kCommonWaitMs, kCommonPollMs); - sendDoubleTap(gWindow, 42, 100); - - yield waitForCondition(function () { - return !SelectionHelperUI.isSelectionUIVisible; - }, kCommonWaitMs, kCommonPollMs); - - // check copy text results - let text = SpecialPowers.getClipboardData("text/unicode"); - is(text, "suddenly", "copy text test"); + sendTap(gWindow, 30, 100); - // check for active selection - is(getTrimmedSelection(gWindow).toString(), "", "selection test"); - }, - tearDown: function tearDown() { - emptyClipboard(); - clearSelection(gWindow); - let scrollPromise = waitForEvent(gWindow, "scroll"); - gWindow.scrollBy(0, -200); - yield scrollPromise; yield waitForCondition(function () { return !SelectionHelperUI.isSelectionUIVisible; }, kCommonWaitMs, kCommonPollMs); }, + tearDown: setUpAndTearDown, }); -gTests.push({ - desc: "single clicks on selection in non-editable content", - setUp: setUpHelper, - run: function test() { - sendContextMenuClick(100, 20); - - yield waitForCondition(function () { - return SelectionHelperUI.isSelectionUIVisible; - }, kCommonWaitMs, kCommonPollMs); - - // active state - is(SelectionHelperUI.isActive, true, "selection active"); - - let ypos = SelectionHelperUI.endMark.yPos + kMarkerOffsetY; - let touchdrag = new TouchDragAndHold(); - yield touchdrag.start(gWindow, SelectionHelperUI.endMark.xPos, ypos, 190, ypos); - touchdrag.end(); - - yield waitForCondition(function () { - return !SelectionHelperUI.hasActiveDrag; - }, kCommonWaitMs, kCommonPollMs); - yield SelectionHelperUI.pingSelectionHandler(); - - // active state - is(SelectionHelperUI.isActive, true, "selection active"); - - // click on selected text - nothing should change - sendTap(gWindow, 240, 20); - - is(SelectionHelperUI.isActive, true, "selection active"); - - // click outside the text - nothing should change - sendTap(gWindow, 197, 119); - - is(SelectionHelperUI.isActive, true, "selection active"); - }, - tearDown: tearDownHelper, -}); -*/ - function test() { if (!isLandscapeMode()) { todo(false, "browser_selection_tests need landscape mode to run."); return; } requestLongerTimeout(3); runTests();
--- a/browser/metro/shell/testing/metrotestharness.cpp +++ b/browser/metro/shell/testing/metrotestharness.cpp @@ -35,60 +35,71 @@ char buffer[PIPE_BUFFER_SIZE + 1]; CString sAppParams; CString sFirefoxPath; // The tests file we write out for firefox.exe which contains test // startup command line paramters. #define kMetroTestFile "tests.ini" +// Process exit codes for buildbotcustom logic. These are currently ignored, but +// at some point releng expects to use these. +#define SUCCESS 0 +#define WARNINGS 1 +#define FAILURE 2 +#define EXCEPTION 3 +#define RETRY 4 + static void Log(const wchar_t *fmt, ...) { va_list a = NULL; wchar_t szDebugString[1024]; if(!lstrlenW(fmt)) return; va_start(a,fmt); vswprintf(szDebugString, 1024, fmt, a); va_end(a); if(!lstrlenW(szDebugString)) return; wprintf(L"INFO | metrotestharness.exe | %s\n", szDebugString); fflush(stdout); } -static void Fail(const wchar_t *fmt, ...) +static void Fail(bool aRequestRetry, const wchar_t *fmt, ...) { va_list a = NULL; wchar_t szDebugString[1024]; if(!lstrlenW(fmt)) return; va_start(a,fmt); vswprintf(szDebugString, 1024, fmt, a); va_end(a); if(!lstrlenW(szDebugString)) return; - - wprintf(L"TEST-UNEXPECTED-FAIL | metrotestharness.exe | %s\n", szDebugString); + if (aRequestRetry) { + wprintf(L"FAIL-SHOULD-RETRY | metrotestharness.exe | %s\n", szDebugString); + } else { + wprintf(L"TEST-UNEXPECTED-FAIL | metrotestharness.exe | %s\n", szDebugString); + } fflush(stdout); } /* * Retrieve our module dir path. * * @aPathBuffer Buffer to fill */ static bool GetModulePath(CStringW& aPathBuffer) { WCHAR buffer[MAX_PATH]; memset(buffer, 0, sizeof(buffer)); if (!GetModuleFileName(NULL, buffer, MAX_PATH)) { - Fail(L"GetModuleFileName failed."); + Fail(false, L"GetModuleFileName failed."); return false; } WCHAR* slash = wcsrchr(buffer, '\\'); if (!slash) return false; *slash = '\0'; @@ -185,44 +196,37 @@ static void ReadPipe() DWORD numBytesRead; while (ReadFile(gTestOutputPipe, buffer, PIPE_BUFFER_SIZE, &numBytesRead, NULL) && numBytesRead) { buffer[numBytesRead] = '\0'; printf("%s", buffer); } } -// From buildbotcustom logic: -#define SUCCESS 0 -#define WARNINGS 1 -#define FAILURE 2 -#define EXCEPTION 3 -#define RETRY 4 /* will retry endlessly on new slaves, be careful with this! */ - static int Launch() { Log(L"Launching browser..."); DWORD processID; // The interface that allows us to activate the browser CComPtr<IApplicationActivationManager> activateMgr; if (FAILED(CoCreateInstance(CLSID_ApplicationActivationManager, NULL, CLSCTX_LOCAL_SERVER, IID_IApplicationActivationManager, (void**)&activateMgr))) { - Fail(L"CoCreateInstance CLSID_ApplicationActivationManager failed."); + Fail(false, L"CoCreateInstance CLSID_ApplicationActivationManager failed."); return FAILURE; } HRESULT hr; WCHAR appModelID[256]; // Activation is based on the browser's registered app model id if (!GetDefaultBrowserAppModelID(appModelID, (sizeof(appModelID)/sizeof(WCHAR)))) { - Fail(L"GetDefaultBrowserAppModelID failed."); + Fail(false, L"GetDefaultBrowserAppModelID failed."); return FAILURE; } Log(L"App model id='%s'", appModelID); // Hand off focus rights if the terminal has focus to the out-of-process // activation server (explorer.exe). Without this the metro interface // won't launch. hr = CoAllowSetForegroundWindow(activateMgr, NULL); @@ -246,93 +250,93 @@ static int Launch() // Because we can't pass command line args, we store params in a // tests.ini file in dist/bin which the browser picks up on launch. CStringA testFilePath; if (sFirefoxPath.GetLength()) { // Use the firefoxpath passed to us by the test harness int index = sFirefoxPath.ReverseFind('\\'); if (index == -1) { - Fail(L"Bad firefoxpath path"); + Fail(false, L"Bad firefoxpath path"); return FAILURE; } testFilePath = sFirefoxPath.Mid(0, index); testFilePath += "\\"; testFilePath += kMetroTestFile; } else { // Use the module path char path[MAX_PATH]; if (!GetModuleFileNameA(NULL, path, MAX_PATH)) { - Fail(L"GetModuleFileNameA errorno=%d", GetLastError()); + Fail(false, L"GetModuleFileNameA errorno=%d", GetLastError()); return FAILURE; } char* slash = strrchr(path, '\\'); if (!slash) return FAILURE; *slash = '\0'; // no trailing slash testFilePath = path; testFilePath += "\\"; sFirefoxPath = testFilePath; sFirefoxPath += kFirefoxExe; testFilePath += kMetroTestFile; } // Make sure the firefox bin exists if (GetFileAttributesW(sFirefoxPath) == INVALID_FILE_ATTRIBUTES) { - Fail(L"Invalid bin path: '%s'", sFirefoxPath); + Fail(false, L"Invalid bin path: '%s'", sFirefoxPath); return FAILURE; } Log(L"Using bin path: '%s'", sFirefoxPath); Log(L"Writing out tests.ini to: '%s'", CStringW(testFilePath)); HANDLE hTestFile = CreateFileA(testFilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hTestFile == INVALID_HANDLE_VALUE) { - Fail(L"CreateFileA errorno=%d", GetLastError()); + Fail(false, L"CreateFileA errorno=%d", GetLastError()); return FAILURE; } DeleteTestFileHelper dtf(testFilePath); // nsAppRunner expects the first param to be the bin path, just like a // normal startup. So prepend our bin path to our param string we write. CStringA asciiParams = sFirefoxPath; asciiParams += " "; asciiParams += sAppParams; asciiParams.Trim(); Log(L"Browser command line args: '%s'", CString(asciiParams)); if (!WriteFile(hTestFile, asciiParams, asciiParams.GetLength(), NULL, 0)) { CloseHandle(hTestFile); - Fail(L"WriteFile errorno=%d", GetLastError()); + Fail(false, L"WriteFile errorno=%d", GetLastError()); return FAILURE; } FlushFileBuffers(hTestFile); CloseHandle(hTestFile); // Create a named stdout pipe for the browser if (!SetupTestOutputPipe()) { - Fail(L"SetupTestOutputPipe failed (errno=%d)", GetLastError()); + Fail(false, L"SetupTestOutputPipe failed (errno=%d)", GetLastError()); return FAILURE; } // Launch firefox hr = activateMgr->ActivateApplication(appModelID, L"", AO_NOERRORUI, &processID); if (FAILED(hr)) { - Fail(L"ActivateApplication result %X", hr); + Fail(true, L"ActivateApplication result %X", hr); return RETRY; } Log(L"Activation succeeded. processid=%d", processID); HANDLE child = OpenProcess(SYNCHRONIZE, FALSE, processID); if (!child) { - Fail(L"Couldn't find child process. (%d)", GetLastError()); + Fail(false, L"Couldn't find child process. (%d)", GetLastError()); return FAILURE; } Log(L"Waiting on child process..."); MSG msg; DWORD waitResult = WAIT_TIMEOUT; HANDLE handles[2] = { child, gTestOutputPipe };
--- a/build/autoconf/android.m4 +++ b/build/autoconf/android.m4 @@ -300,25 +300,40 @@ case "$target" in AC_MSG_ERROR([The given Android SDK provides API level $android_api_level ($1 or higher required).]) fi fi android_platform_tools="$android_sdk"/../../platform-tools if test ! -d "$android_platform_tools" ; then android_platform_tools="$android_sdk"/tools # SDK Tools < r8 fi + # The build tools got moved around to different directories in + # SDK Tools r22. Try to locate them. + android_build_tools="" + for suffix in 17.0.0 android-4.2.2; do + tools_directory="$android_sdk/../../build-tools/$suffix" + if test -d "$tools_directory" ; then + android_build_tools="$tools_directory" + break + fi + done + if test -z "$android_build_tools" ; then + android_build_tools="$android_platform_tools" # SDK Tools < r22 + fi ANDROID_SDK="${android_sdk}" if test -e "${android_sdk}/../../extras/android/compatibility/v4/android-support-v4.jar" ; then ANDROID_COMPAT_LIB="${android_sdk}/../../extras/android/compatibility/v4/android-support-v4.jar" else ANDROID_COMPAT_LIB="${android_sdk}/../../extras/android/support/v4/android-support-v4.jar"; fi ANDROID_PLATFORM_TOOLS="${android_platform_tools}" + ANDROID_BUILD_TOOLS="${android_build_tools}" AC_SUBST(ANDROID_SDK) AC_SUBST(ANDROID_COMPAT_LIB) if ! test -e $ANDROID_COMPAT_LIB ; then AC_MSG_ERROR([You must download the Android support library when targeting Android. Run the Android SDK tool and install Android Support Library under Extras. See https://developer.android.com/tools/extras/support-library.html for more info. (looked for $ANDROID_COMPAT_LIB)]) fi AC_SUBST(ANDROID_PLATFORM_TOOLS) + AC_SUBST(ANDROID_BUILD_TOOLS) ;; esac ])
--- a/build/mobile/robocop/Makefile.in +++ b/build/mobile/robocop/Makefile.in @@ -8,16 +8,18 @@ srcdir = @srcdir@ VPATH = @srcdir@ mobile-tests := mobile/android/base/tests TESTPATH := $(topsrcdir)/$(mobile-tests) dir-tests := $(DEPTH)/$(mobile-tests) include $(DEPTH)/config/autoconf.mk +ANDROID_APK_NAME := robocop-debug + ROBOTIUM_PATH = $(srcdir)/robotium-solo-3.6.jar JAVAFILES = \ R.java \ $(NULL) RES_FILES = \ res/values/strings.xml \ @@ -76,49 +78,55 @@ MOCHITEST_ROBOCOP_FILES := \ $(wildcard $(TESTPATH)/*.xml) \ $(NULL) GARBAGE += \ AndroidManifest.xml \ $(java-tests-dep) \ $(_JAVA_HARNESS) \ classes.dex \ - robocop.ap_ \ - robocop-debug-signed.apk \ - robocop-debug-signed-unaligned.apk \ + $(ANDROID_APK_NAME).ap_ \ + $(ANDROID_APK_NAME)-unsigned-unaligned.apk \ + $(ANDROID_APK_NAME)-unaligned.apk \ + $(ANDROID_APK_NAME).apk \ $(robocop-deps) \ $(NULL) DEFINES += \ -DANDROID_PACKAGE_NAME=$(ANDROID_PACKAGE_NAME) \ $(NULL) JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar:$(ROBOTIUM_PATH) include $(topsrcdir)/config/rules.mk # Override rules.mk java flags with the android specific ones include $(topsrcdir)/config/android-common.mk GENERATED_DIRS_tools = classes $(dir-tests) -libs:: robocop-debug-signed.apk +tools:: $(ANDROID_APK_NAME).apk -classes.dex: robocop.ap_ +classes.dex: $(ANDROID_APK_NAME).ap_ classes.dex: $(robocop-deps) classes.dex: $(java-harness-dep) classes.dex: $(java-tests-dep) $(JAVAC) $(JAVAC_FLAGS) -d classes $(JAVAFILES) $(_JAVA_HARNESS) $(java-tests-dep) $(DX) --dex --output=$@ classes $(ROBOTIUM_PATH) $(ANDROID_COMPT_LIB) -robocop.ap_: AndroidManifest.xml $(TESTPATH)/assets/* +$(ANDROID_APK_NAME).ap_: AndroidManifest.xml $(TESTPATH)/assets/* $(AAPT) package -f -M $< -I $(ANDROID_SDK)/android.jar -I . -S res -A $(TESTPATH)/assets -F $@ -J ./ -robocop-debug-signed-unaligned.apk: robocop.ap_ classes.dex - $(APKBUILDER) $@ -v $(APKBUILDER_FLAGS) -z robocop.ap_ -f classes.dex +$(ANDROID_APK_NAME)-unsigned-unaligned.apk: $(ANDROID_APK_NAME).ap_ classes.dex + cp $< $@ + $(ZIP) -v0 $@ classes.dex -robocop-debug-signed.apk: robocop-debug-signed-unaligned.apk - $(ZIPALIGN) -f -v 4 $^ $@ +$(ANDROID_APK_NAME)-unaligned.apk: $(ANDROID_APK_NAME)-unsigned-unaligned.apk + cp $< $@ + $(DEBUG_JARSIGNER) $@ + +$(ANDROID_APK_NAME).apk: $(ANDROID_APK_NAME)-unaligned.apk + $(ZIPALIGN) -f -v 4 $< $@ # PP_java-tests not fully usable here # Intermediate step toward a library rule. $(dir-tests)/%.java: $(TESTPATH)/%.java.in $(call mkdir_deps,$(dir-tests)) $(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $< > $@
--- a/build/mobile/sutagent/android/Makefile.in +++ b/build/mobile/sutagent/android/Makefile.in @@ -4,16 +4,18 @@ DEPTH = @DEPTH@ topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk +ANDROID_APK_NAME := sutAgentAndroid + JAVAFILES = \ AlertLooperThread.java \ ASMozStub.java \ CmdWorkerThread.java \ DataWorkerThread.java \ DoAlert.java \ DoCommand.java \ FindProcThread.java \ @@ -34,45 +36,44 @@ RES_FILES = \ res/drawable/ic_stat_warning.png \ res/layout/main.xml \ res/values/strings.xml \ $(NULL) GARBAGE += \ AndroidManifest.xml \ classes.dex \ - sutAgentAndroid.apk \ - sutAgentAndroid.ap_ \ - sutAgentAndroid-unsigned-unaligned.apk \ - sutAgentAndroid-unaligned.apk \ + $(ANDROID_APK_NAME).ap_ \ + $(ANDROID_APK_NAME)-unsigned-unaligned.apk \ + $(ANDROID_APK_NAME)-unaligned.apk \ + $(ANDROID_APK_NAME).apk \ $(NULL) GARBAGE_DIRS += network-libs EXTRA_JARS = $(srcdir)/network-libs/commons-net-2.0.jar:$(srcdir)/network-libs/jmdns.jar JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar:$(EXTRA_JARS) include $(topsrcdir)/config/rules.mk # include Android specific java flags - using these instead of what's in rules.mk include $(topsrcdir)/config/android-common.mk -tools:: sutAgentAndroid.apk +tools:: $(ANDROID_APK_NAME).apk classes.dex: $(JAVAFILES) $(JAVAC) $(JAVAC_FLAGS) -d classes $(addprefix $(srcdir)/,$(JAVAFILES)) $(DX) --dex --output=$@ classes $(subst :, ,$(EXTRA_JARS)) -sutAgentAndroid.ap_: $(srcdir)/AndroidManifest.xml +$(ANDROID_APK_NAME).ap_: AndroidManifest.xml $(AAPT) package -f -M $< -I $(ANDROID_SDK)/android.jar -S res -F $@ -sutAgentAndroid-unsigned-unaligned.apk: sutAgentAndroid.ap_ classes.dex - $(APKBUILDER) $@ -v $(APKBUILDER_FLAGS) -z sutAgentAndroid.ap_ -f classes.dex - -sutAgentAndroid-unaligned.apk: sutAgentAndroid-unsigned-unaligned.apk +$(ANDROID_APK_NAME)-unsigned-unaligned.apk: $(ANDROID_APK_NAME).ap_ classes.dex cp $< $@ -ifdef JARSIGNER - $(JARSIGNER) $@ -endif + $(ZIP) -v0 $@ classes.dex -sutAgentAndroid.apk: sutAgentAndroid-unaligned.apk - $(ZIPALIGN) -f -v 4 sutAgentAndroid-unaligned.apk $@ +$(ANDROID_APK_NAME)-unaligned.apk: $(ANDROID_APK_NAME)-unsigned-unaligned.apk + cp $< $@ + $(DEBUG_JARSIGNER) $@ + +$(ANDROID_APK_NAME).apk: $(ANDROID_APK_NAME)-unaligned.apk + $(ZIPALIGN) -f -v 4 $< $@
--- a/build/mobile/sutagent/android/fencp/Makefile.in +++ b/build/mobile/sutagent/android/fencp/Makefile.in @@ -4,16 +4,18 @@ DEPTH = @DEPTH@ topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk +ANDROID_APK_NAME := FenCP + JAVAFILES = \ DirCursor.java \ FenCP.java \ FenCPFP.java \ FileCursor.java \ R.java \ $(NULL) @@ -23,40 +25,42 @@ RES_FILES = \ res/drawable-mdpi/icon.png \ res/layout/main.xml \ res/values/strings.xml \ $(NULL) GARBAGE += \ AndroidManifest.xml \ classes.dex \ - FenCP.apk \ + $(ANDROID_APK_NAME).ap_ \ + $(ANDROID_APK_NAME)-unsigned-unaligned.apk \ + $(ANDROID_APK_NAME)-unaligned.apk \ + $(ANDROID_APK_NAME).apk \ $(NULL) GARBAGE_DIRS += network-libs JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar include $(topsrcdir)/config/rules.mk # include Android specific java flags - using these instead of what's in rules.mk include $(topsrcdir)/config/android-common.mk -tools:: FenCP.apk +tools:: $(ANDROID_APK_NAME).apk classes.dex: $(JAVAFILES) $(JAVAC) $(JAVAC_FLAGS) -d classes $(addprefix $(srcdir)/,$(JAVAFILES)) $(DX) --dex --output=$@ classes -FenCP.ap_: $(srcdir)/AndroidManifest.xml - $(AAPT) package -f -M $(srcdir)/AndroidManifest.xml -I $(ANDROID_SDK)/android.jar -S res -F $@ +$(ANDROID_APK_NAME).ap_: AndroidManifest.xml + $(AAPT) package -f -M $< -I $(ANDROID_SDK)/android.jar -S res -F $@ -FenCP-unsigned-unaligned.apk: FenCP.ap_ classes.dex - $(APKBUILDER) $@ -v $(APKBUILDER_FLAGS) -z FenCP.ap_ -f classes.dex +$(ANDROID_APK_NAME)-unsigned-unaligned.apk: $(ANDROID_APK_NAME).ap_ classes.dex + cp $< $@ + $(ZIP) -v0 $@ classes.dex -FenCP-unaligned.apk: FenCP-unsigned-unaligned.apk - cp FenCP-unsigned-unaligned.apk $@ -ifdef JARSIGNER - $(JARSIGNER) $@ -endif +$(ANDROID_APK_NAME)-unaligned.apk: $(ANDROID_APK_NAME)-unsigned-unaligned.apk + cp $< $@ + $(DEBUG_JARSIGNER) $@ -FenCP.apk: FenCP-unaligned.apk - $(ZIPALIGN) -f -v 4 FenCP-unaligned.apk $@ +$(ANDROID_APK_NAME).apk: $(ANDROID_APK_NAME)-unaligned.apk + $(ZIPALIGN) -f -v 4 $< $@
--- a/build/mobile/sutagent/android/ffxcp/Makefile.in +++ b/build/mobile/sutagent/android/ffxcp/Makefile.in @@ -4,16 +4,18 @@ DEPTH = @DEPTH@ topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk +ANDROID_APK_NAME := FfxCP + JAVAFILES = \ DirCursor.java \ ffxcp.java \ FfxCPFP.java \ FileCursor.java \ R.java \ $(NULL) @@ -23,40 +25,42 @@ RES_FILES = \ res/drawable-mdpi/icon.png \ res/layout/main.xml \ res/values/strings.xml \ $(NULL) GARBAGE += \ AndroidManifest.xml \ classes.dex \ - FfxCP.apk \ + $(ANDROID_APK_NAME).ap_ \ + $(ANDROID_APK_NAME)-unsigned-unaligned.apk \ + $(ANDROID_APK_NAME)-unaligned.apk \ + $(ANDROID_APK_NAME).apk \ $(NULL) GARBAGE_DIRS += network-libs JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar include $(topsrcdir)/config/rules.mk # include Android specific java flags - using these instead of what's in rules.mk include $(topsrcdir)/config/android-common.mk -tools:: FfxCP.apk +tools:: $(ANDROID_APK_NAME).apk classes.dex: $(JAVAFILES) $(JAVAC) $(JAVAC_FLAGS) -d classes $(addprefix $(srcdir)/,$(JAVAFILES)) $(DX) --dex --output=$@ classes -FfxCP.ap_: $(srcdir)/AndroidManifest.xml - $(AAPT) package -f -M $(srcdir)/AndroidManifest.xml -I $(ANDROID_SDK)/android.jar -S res -F $@ +$(ANDROID_APK_NAME).ap_: AndroidManifest.xml + $(AAPT) package -f -M $< -I $(ANDROID_SDK)/android.jar -S res -F $@ -FfxCP-unsigned-unaligned.apk: FfxCP.ap_ classes.dex - $(APKBUILDER) $@ -v $(APKBUILDER_FLAGS) -z FfxCP.ap_ -f classes.dex +$(ANDROID_APK_NAME)-unsigned-unaligned.apk: $(ANDROID_APK_NAME).ap_ classes.dex + cp $< $@ + $(ZIP) -v0 $@ classes.dex -FfxCP-unaligned.apk: FfxCP-unsigned-unaligned.apk - cp FfxCP-unsigned-unaligned.apk $@ -ifdef JARSIGNER - $(JARSIGNER) $@ -endif +$(ANDROID_APK_NAME)-unaligned.apk: $(ANDROID_APK_NAME)-unsigned-unaligned.apk + cp $< $@ + $(DEBUG_JARSIGNER) $@ -FfxCP.apk: FfxCP-unaligned.apk - $(ZIPALIGN) -f -v 4 FfxCP-unaligned.apk $@ +$(ANDROID_APK_NAME).apk: $(ANDROID_APK_NAME)-unaligned.apk + $(ZIPALIGN) -f -v 4 $< $@
--- a/build/mobile/sutagent/android/watcher/Makefile.in +++ b/build/mobile/sutagent/android/watcher/Makefile.in @@ -4,16 +4,18 @@ DEPTH = @DEPTH@ topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk +ANDROID_APK_NAME := Watcher + JAVAFILES = \ IWatcherService.java \ RedirOutputThread.java \ R.java \ WatcherMain.java \ WatcherReceiver.java \ WatcherService.java \ $(NULL) @@ -27,46 +29,43 @@ RES_FILES = \ res/drawable-mdpi/ateamlogo.png \ res/layout/main.xml \ res/values/strings.xml \ $(NULL) GARBAGE += \ AndroidManifest.xml \ classes.dex \ - Watcher.apk \ + $(ANDROID_APK_NAME).ap_ \ + $(ANDROID_APK_NAME)-unsigned-unaligned.apk \ + $(ANDROID_APK_NAME)-unaligned.apk \ + $(ANDROID_APK_NAME).apk \ $(NULL) GARBAGE_DIRS += res classes network-libs JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar include $(topsrcdir)/config/rules.mk # include Android specific java flags - using these instead of what's in rules.mk include $(topsrcdir)/config/android-common.mk -tools:: Watcher.apk +tools:: $(ANDROID_APK_NAME).apk classes.dex: $(JAVAFILES) $(NSINSTALL) -D classes $(JAVAC) $(JAVAC_FLAGS) -d classes $(addprefix $(srcdir)/,$(JAVAFILES)) $(DX) --dex --output=$@ classes -Watcher.ap_: $(srcdir)/AndroidManifest.xml - $(AAPT) package -f -M $(srcdir)/AndroidManifest.xml -I $(ANDROID_SDK)/android.jar -S res -F $@ +$(ANDROID_APK_NAME).ap_: AndroidManifest.xml + $(AAPT) package -f -M $< -I $(ANDROID_SDK)/android.jar -S res -F $@ -Watcher-unsigned-unaligned.apk: Watcher.ap_ classes.dex - $(APKBUILDER) $@ -v $(APKBUILDER_FLAGS) -z Watcher.ap_ -f classes.dex +$(ANDROID_APK_NAME)-unsigned-unaligned.apk: $(ANDROID_APK_NAME).ap_ classes.dex + cp $< $@ + $(ZIP) -v0 $@ classes.dex -Watcher-unaligned.apk: Watcher-unsigned-unaligned.apk - cp Watcher-unsigned-unaligned.apk $@ -ifdef JARSIGNER - $(JARSIGNER) $@ -endif +$(ANDROID_APK_NAME)-unaligned.apk: $(ANDROID_APK_NAME)-unsigned-unaligned.apk + cp $< $@ + $(DEBUG_JARSIGNER) $@ -Watcher.apk: Watcher-unaligned.apk - $(ZIPALIGN) -f -v 4 Watcher-unaligned.apk $@ - -export:: - $(NSINSTALL) -D res - @(cd $(srcdir)/res && tar $(TAR_CREATE_FLAGS) - *) | (cd $(DEPTH)/build/mobile/sutagent/android/watcher/res && tar -xf -) - +$(ANDROID_APK_NAME).apk: $(ANDROID_APK_NAME)-unaligned.apk + $(ZIPALIGN) -f -v 4 $< $@
--- a/config/Makefile.in +++ b/config/Makefile.in @@ -16,17 +16,16 @@ include $(DEPTH)/config/autoconf.mk VISIBILITY_FLAGS = # STDCXX_COMPAT is not needed here, and will actually fail because # libstdc++-compat is not built yet. STDCXX_COMPAT = ifneq (WINNT,$(HOST_OS_ARCH)) HOST_PROGRAM = nsinstall_real$(HOST_BIN_SUFFIX) -DISABLED_HOST_CSRCS = nsinstall.c pathsub.c endif ifndef CROSS_COMPILE ifdef USE_ELF_DYNSTR_GC export:: elf-dynstr-gc endif endif
--- a/config/android-common.mk +++ b/config/android-common.mk @@ -7,24 +7,21 @@ ifndef ANDROID_SDK $(error ANDROID_SDK must be defined before including android-common.mk) endif ifndef JAVA_CLASSPATH $(error JAVA_CLASSPATH must be defined before including android-common.mk) endif -DX=$(ANDROID_PLATFORM_TOOLS)/dx -AAPT=$(ANDROID_PLATFORM_TOOLS)/aapt -APKBUILDER=$(ANDROID_SDK)/../../tools/apkbuilder +DX=$(ANDROID_BUILD_TOOLS)/dx +AAPT=$(ANDROID_BUILD_TOOLS)/aapt ZIPALIGN=$(ANDROID_SDK)/../../tools/zipalign - -ifdef JARSIGNER - APKBUILDER_FLAGS += -u -endif +# DEBUG_JARSIGNER always debug signs. +DEBUG_JARSIGNER=$(PYTHON) $(call core_abspath,$(topsrcdir)/mobile/android/debug_sign_tool.py) # For Android, this defaults to $(ANDROID_SDK)/android.jar ifndef JAVA_BOOTCLASSPATH JAVA_BOOTCLASSPATH = $(ANDROID_SDK)/android.jar:$(ANDROID_COMPAT_LIB) endif # For Android, we default to 1.5 ifndef JAVA_VERSION
--- a/config/rules.mk +++ b/config/rules.mk @@ -11,16 +11,17 @@ ifndef topsrcdir endif # Integrate with mozbuild-generated make files. We first verify that no # variables provided by the automatically generated .mk files are # present. If they are, this is a violation of the separation of # responsibility between Makefile.in and mozbuild files. _MOZBUILD_EXTERNAL_VARIABLES := \ DIRS \ + HOST_CSRCS \ MODULE \ PARALLEL_DIRS \ TEST_DIRS \ TIERS \ TOOL_DIRS \ XPIDL_MODULE \ $(NULL)
--- a/content/base/public/nsContentUtils.h +++ b/content/base/public/nsContentUtils.h @@ -505,27 +505,16 @@ public: * @return boolean indicating whether a BOM was detected. */ static bool CheckForBOM(const unsigned char* aBuffer, uint32_t aLength, nsACString& aCharset); static nsresult GuessCharset(const char *aData, uint32_t aDataLen, nsACString &aCharset); - /** - * Determine whether aContent is in some way associated with aForm. If the - * form is a container the only elements that are considered to be associated - * with a form are the elements that are contained within the form. If the - * form is a leaf element then all elements will be accepted into this list, - * since this can happen due to content fixup when a form spans table rows or - * table cells. - */ - static bool BelongsInForm(nsIContent *aForm, - nsIContent *aContent); - static nsresult CheckQName(const nsAString& aQualifiedName, bool aNamespaceAware = true, const PRUnichar** aColon = nullptr); static nsresult SplitQName(const nsIContent* aNamespaceResolver, const nsAFlatString& aQName, int32_t *aNamespace, nsIAtom **aLocalName);
--- a/content/base/src/nsContentList.cpp +++ b/content/base/src/nsContentList.cpp @@ -135,36 +135,16 @@ NS_IMPL_ADDREF_INHERITED(nsSimpleContent NS_IMPL_RELEASE_INHERITED(nsSimpleContentList, nsBaseContentList) JSObject* nsSimpleContentList::WrapObject(JSContext *cx, JS::Handle<JSObject*> scope) { return NodeListBinding::Wrap(cx, scope, this); } -// nsFormContentList - -nsFormContentList::nsFormContentList(nsIContent *aForm, - nsBaseContentList& aContentList) - : nsSimpleContentList(aForm) -{ - - // move elements that belong to mForm into this content list - - uint32_t i, length = 0; - aContentList.GetLength(&length); - - for (i = 0; i < length; i++) { - nsIContent *c = aContentList.Item(i); - if (c && nsContentUtils::BelongsInForm(aForm, c)) { - AppendElement(c); - } - } -} - // Hashtable for storing nsContentLists static PLDHashTable gContentListHashTable; struct ContentListHashEntry : public PLDHashEntryHdr { nsContentList* mContentList; };
--- a/content/base/src/nsContentList.h +++ b/content/base/src/nsContentList.h @@ -119,26 +119,16 @@ public: virtual JSObject* WrapObject(JSContext *cx, JS::Handle<JSObject*> scope) MOZ_OVERRIDE; private: // This has to be a strong reference, the root might go away before the list. nsCOMPtr<nsINode> mRoot; }; -// This class is used only by form element code and this is a static -// list of elements. NOTE! This list holds strong references to -// the elements in the list. -class nsFormContentList : public nsSimpleContentList -{ -public: - nsFormContentList(nsIContent *aForm, - nsBaseContentList& aContentList); -}; - /** * Class that's used as the key to hash nsContentList implementations * for fast retrieval */ struct nsContentListKey { nsContentListKey(nsINode* aRootNode, int32_t aMatchNameSpaceId,
--- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -2361,72 +2361,16 @@ nsContentUtils::NewURIWithDocumentCharse nsIURI* aBaseURI) { return NS_NewURI(aResult, aSpec, aDocument ? aDocument->GetDocumentCharacterSet().get() : nullptr, aBaseURI, sIOService); } // static -bool -nsContentUtils::BelongsInForm(nsIContent *aForm, - nsIContent *aContent) -{ - NS_PRECONDITION(aForm, "Must have a form"); - NS_PRECONDITION(aContent, "Must have a content node"); - - if (aForm == aContent) { - // A form does not belong inside itself, so we return false here - - return false; - } - - nsIContent* content = aContent->GetParent(); - - while (content) { - if (content == aForm) { - // aContent is contained within the form so we return true. - - return true; - } - - if (content->Tag() == nsGkAtoms::form && - content->IsHTML()) { - // The child is contained within a form, but not the right form - // so we ignore it. - - return false; - } - - content = content->GetParent(); - } - - if (aForm->GetChildCount() > 0) { - // The form is a container but aContent wasn't inside the form, - // return false - - return false; - } - - // The form is a leaf and aContent wasn't inside any other form so - // we check whether the content comes after the form. If it does, - // return true. If it does not, then it couldn't have been inside - // the form in the HTML. - if (PositionIsBefore(aForm, aContent)) { - // We could be in this form! - // In the future, we may want to get document.forms, look at the - // form after aForm, and if aContent is after that form after - // aForm return false here.... - return true; - } - - return false; -} - -// static nsresult nsContentUtils::CheckQName(const nsAString& aQualifiedName, bool aNamespaceAware, const PRUnichar** aColon) { const char* colon = nullptr; const PRUnichar* begin = aQualifiedName.BeginReading(); const PRUnichar* end = aQualifiedName.EndReading();
--- a/content/base/src/nsDOMFileReader.h +++ b/content/base/src/nsDOMFileReader.h @@ -140,12 +140,12 @@ protected: nsCString mCharset; uint32_t mDataLen; eDataFormat mDataFormat; nsString mResult; nsCOMPtr<nsIPrincipal> mPrincipal; - JSObject* mResultArrayBuffer; + JS::Heap<JSObject*> mResultArrayBuffer; }; #endif
--- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -1781,17 +1781,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END struct CustomPrototypeTraceArgs { const TraceCallbacks& callbacks; void* closure; }; static PLDHashOperator -CustomPrototypeTrace(const nsAString& aName, JSObject*& aObject, void *aArg) +CustomPrototypeTrace(const nsAString& aName, JS::Heap<JSObject*>& aObject, void *aArg) { CustomPrototypeTraceArgs* traceArgs = static_cast<CustomPrototypeTraceArgs*>(aArg); MOZ_ASSERT(aObject, "Protocol object value must not be null"); traceArgs->callbacks.Trace(&aObject, "mCustomPrototypes entry", traceArgs->closure); return PL_DHASH_NEXT; }
--- a/content/base/src/nsDocument.h +++ b/content/base/src/nsDocument.h @@ -26,16 +26,17 @@ #include "nsIContent.h" #include "nsEventListenerManager.h" #include "nsIDOMNodeSelector.h" #include "nsIPrincipal.h" #include "nsIParser.h" #include "nsBindingManager.h" #include "nsINodeInfo.h" #include "nsInterfaceHashtable.h" +#include "nsJSThingHashtable.h" #include "nsIBoxObject.h" #include "nsPIBoxObject.h" #include "nsIScriptObjectPrincipal.h" #include "nsIURI.h" #include "nsScriptLoader.h" #include "nsIRadioGroupContainer.h" #include "nsILayoutHistoryState.h" #include "nsIRequest.h" @@ -1198,17 +1199,17 @@ protected: nsTArray<nsWeakPtr> mFullScreenStack; // The root of the doc tree in which this document is in. This is only // non-null when this document is in fullscreen mode. nsWeakPtr mFullscreenRoot; // Hashtable for custom element prototypes in web components. // Custom prototypes are in the document's compartment. - nsDataHashtable<nsStringHashKey, JSObject*> mCustomPrototypes; + nsJSThingHashtable<nsStringHashKey, JSObject*> mCustomPrototypes; nsRefPtr<nsEventListenerManager> mListenerManager; nsCOMPtr<nsIDOMStyleSheetList> mDOMStyleSheets; nsRefPtr<nsDOMStyleSheetSetList> mStyleSheetSetList; nsRefPtr<nsScriptLoader> mScriptLoader; nsDocHeaderData* mHeaderData; /* mIdentifierMap works as follows for IDs: * 1) Attribute changes affect the table immediately (removing and adding
--- a/content/base/src/nsNodeUtils.cpp +++ b/content/base/src/nsNodeUtils.cpp @@ -20,16 +20,17 @@ #include "nsCOMArray.h" #include "nsPIDOMWindow.h" #include "nsDocument.h" #ifdef MOZ_XUL #include "nsXULElement.h" #endif #include "nsBindingManager.h" #include "nsGenericHTMLElement.h" +#include "mozilla/dom/HTMLImageElement.h" #include "mozilla/dom/HTMLMediaElement.h" #include "nsWrapperCacheInlines.h" #include "nsObjectLoadingContent.h" #include "nsDOMMutationObserver.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/HTMLTemplateElement.h" using namespace mozilla::dom; @@ -211,16 +212,21 @@ nsNodeUtils::LastRelease(nsINode* aNode) // I wonder whether it's faster to do the HasFlag check first.... if (aNode->IsNodeOfType(nsINode::eHTML_FORM_CONTROL) && aNode->HasFlag(ADDED_TO_FORM)) { // Tell the form (if any) this node is going away. Don't // notify, since we're being destroyed in any case. static_cast<nsGenericHTMLFormElement*>(aNode)->ClearForm(true); } + + if (aNode->IsElement() && aNode->AsElement()->IsHTML(nsGkAtoms::img)) { + HTMLImageElement* imageElem = static_cast<HTMLImageElement*>(aNode); + imageElem->ClearForm(true); + } } aNode->UnsetFlags(NODE_HAS_PROPERTIES); if (aNode->NodeType() != nsIDOMNode::DOCUMENT_NODE && aNode->HasFlag(NODE_HAS_LISTENERMANAGER)) { #ifdef DEBUG if (nsContentUtils::IsInitialized()) { nsEventListenerManager* manager =
--- a/content/base/src/nsXMLHttpRequest.cpp +++ b/content/base/src/nsXMLHttpRequest.cpp @@ -777,22 +777,24 @@ nsresult nsXMLHttpRequest::CreateResponseParsedJSON(JSContext* aCx) { if (!aCx) { return NS_ERROR_FAILURE; } RootJSResultObjects(); // The Unicode converter has already zapped the BOM if there was one + JS::Rooted<JS::Value> value(aCx); if (!JS_ParseJSON(aCx, static_cast<const jschar*>(mResponseText.get()), mResponseText.Length(), - JS::MutableHandle<JS::Value>::fromMarkedLocation(&mResultJSON))) { + &value)) { return NS_ERROR_FAILURE; } + mResultJSON = value; return NS_OK; } void nsXMLHttpRequest::CreatePartialBlob() { if (mDOMFile) { if (mLoadTotal == mLoadTransferred) {
--- a/content/base/src/nsXMLHttpRequest.h +++ b/content/base/src/nsXMLHttpRequest.h @@ -652,24 +652,24 @@ protected: * @param aType The progress event type. * @param aFlag A XML_HTTP_REQUEST_* state flag defined in * nsXMLHttpRequest.cpp. */ void CloseRequestWithError(const nsAString& aType, const uint32_t aFlag); bool mFirstStartRequestSeen; bool mInLoadProgressEvent; - + nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback; nsCOMPtr<nsIChannel> mNewRedirectChannel; - - JS::Value mResultJSON; + + JS::Heap<JS::Value> mResultJSON; js::ArrayBufferBuilder mArrayBufferBuilder; - JSObject* mResultArrayBuffer; + JS::Heap<JSObject*> mResultArrayBuffer; void ResetResponse(); struct RequestHeader { nsCString header; nsCString value; };
--- a/content/canvas/src/ImageData.h +++ b/content/canvas/src/ImageData.h @@ -64,15 +64,15 @@ public: private: void HoldData(); void DropData(); ImageData() MOZ_DELETE; uint32_t mWidth, mHeight; - JSObject* mData; + JS::Heap<JSObject*> mData; }; } // namespace dom } // namespace mozilla #endif // mozilla_dom_ImageData_h
--- a/content/events/src/nsDOMMessageEvent.h +++ b/content/events/src/nsDOMMessageEvent.h @@ -60,15 +60,15 @@ public: nsIDOMWindow* aSource, mozilla::ErrorResult& aRv) { aRv = InitMessageEvent(aType, aCanBubble, aCancelable, aData, aOrigin, aLastEventId, aSource); } private: - JS::Value mData; + JS::Heap<JS::Value> mData; nsString mOrigin; nsString mLastEventId; nsCOMPtr<nsIDOMWindow> mSource; }; #endif // nsDOMMessageEvent_h__
--- a/content/events/src/nsDOMNotifyAudioAvailableEvent.h +++ b/content/events/src/nsDOMNotifyAudioAvailableEvent.h @@ -65,13 +65,13 @@ public: uint32_t aFrameBufferLength, float aTime, bool aAllowAudioData, mozilla::ErrorResult& aRv); private: nsAutoArrayPtr<float> mFrameBuffer; uint32_t mFrameBufferLength; float mTime; - JSObject* mCachedArray; + JS::Heap<JSObject*> mCachedArray; bool mAllowAudioData; }; #endif // nsDOMNotifyAudioAvailableEvent_h_
--- a/content/html/content/src/HTMLImageElement.cpp +++ b/content/html/content/src/HTMLImageElement.cpp @@ -20,16 +20,17 @@ #include "nsNetUtil.h" #include "nsContentUtils.h" #include "nsIFrame.h" #include "nsNodeInfoManager.h" #include "nsGUIEvent.h" #include "nsContentPolicyUtils.h" #include "nsIDOMWindow.h" #include "nsFocusManager.h" +#include "nsHTMLFormElement.h" #include "imgIContainer.h" #include "imgILoader.h" #include "imgINotificationObserver.h" #include "imgRequestProxy.h" #include "nsILoadGroup.h" @@ -62,16 +63,17 @@ NS_NewHTMLImageElement(already_AddRefed< return new mozilla::dom::HTMLImageElement(nodeInfo.forget()); } namespace mozilla { namespace dom { HTMLImageElement::HTMLImageElement(already_AddRefed<nsINodeInfo> aNodeInfo) : nsGenericHTMLElement(aNodeInfo) + , mForm(nullptr) { // We start out broken AddStatesSilently(NS_EVENT_STATE_BROKEN); SetIsDOMBinding(); } HTMLImageElement::~HTMLImageElement() { @@ -79,17 +81,17 @@ HTMLImageElement::~HTMLImageElement() } NS_IMPL_ADDREF_INHERITED(HTMLImageElement, Element) NS_IMPL_RELEASE_INHERITED(HTMLImageElement, Element) // QueryInterface implementation for HTMLImageElement -NS_INTERFACE_TABLE_HEAD(HTMLImageElement) +NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLImageElement) NS_HTML_CONTENT_INTERFACES(nsGenericHTMLElement) NS_INTERFACE_TABLE_INHERITED4(HTMLImageElement, nsIDOMHTMLImageElement, nsIImageLoadingContent, imgIOnloadBlocker, imgINotificationObserver) NS_INTERFACE_TABLE_TO_MAP_SEGUE NS_ELEMENT_INTERFACE_MAP_END @@ -292,16 +294,57 @@ HTMLImageElement::IsAttributeMapped(cons nsMapRuleToAttributesFunc HTMLImageElement::GetAttributeMappingFunction() const { return &MapAttributesIntoRule; } nsresult +HTMLImageElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, + const nsAttrValueOrString* aValue, + bool aNotify) +{ + + if (aNameSpaceID == kNameSpaceID_None && mForm && + (aName == nsGkAtoms::name || aName == nsGkAtoms::id)) { + // remove the image from the hashtable as needed + nsAutoString tmp; + GetAttr(kNameSpaceID_None, aName, tmp); + + if (!tmp.IsEmpty()) { + mForm->RemoveImageElementFromTable(this, tmp, + nsHTMLFormElement::AttributeUpdated); + } + } + + return nsGenericHTMLElement::BeforeSetAttr(aNameSpaceID, aName, + aValue, aNotify); +} + +nsresult +HTMLImageElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, + const nsAttrValue* aValue, bool aNotify) +{ + if (aNameSpaceID == kNameSpaceID_None && mForm && + (aName == nsGkAtoms::name || aName == nsGkAtoms::id) && + aValue && !aValue->IsEmptyString()) { + // add the image to the hashtable as needed + NS_ABORT_IF_FALSE(aValue->Type() == nsAttrValue::eAtom, + "Expected atom value for name/id"); + mForm->AddImageElementToTable(this, + nsDependentAtomString(aValue->GetAtomValue())); + } + + return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, + aValue, aNotify); +} + + +nsresult HTMLImageElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor) { // If we are a map and get a mouse click, don't let it be handled by // the Generic Element as this could cause a click event to fire // twice, once by the image frame for the map and once by the Anchor // element. (bug 39723) if (aVisitor.mEvent->eventStructType == NS_MOUSE_EVENT && aVisitor.mEvent->message == NS_MOUSE_CLICK && @@ -409,16 +452,20 @@ HTMLImageElement::BindToTree(nsIDocument nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent, aBindingParent, aCompileEventHandlers); NS_ENSURE_SUCCESS(rv, rv); nsImageLoadingContent::BindToTree(aDocument, aParent, aBindingParent, aCompileEventHandlers); + if (aParent) { + UpdateFormOwner(); + } + if (HasAttr(kNameSpaceID_None, nsGkAtoms::src)) { // FIXME: Bug 660963 it would be nice if we could just have // ClearBrokenState update our state and do it fast... ClearBrokenState(); RemoveStatesSilently(NS_EVENT_STATE_BROKEN); // If loading is temporarily disabled, don't even launch MaybeLoadImage. // Otherwise MaybeLoadImage may run later when someone has reenabled // loading. @@ -429,21 +476,56 @@ HTMLImageElement::BindToTree(nsIDocument } return rv; } void HTMLImageElement::UnbindFromTree(bool aDeep, bool aNullParent) { + if (mForm) { + if (aNullParent || !FindAncestorForm(mForm)) { + ClearForm(true); + } else { + UnsetFlags(MAYBE_ORPHAN_FORM_ELEMENT); + } + } + nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent); nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); } void +HTMLImageElement::UpdateFormOwner() +{ + if (!mForm) { + mForm = FindAncestorForm(); + } + + if (mForm && !HasFlag(ADDED_TO_FORM)) { + // Now we need to add ourselves to the form + nsAutoString nameVal, idVal; + GetAttr(kNameSpaceID_None, nsGkAtoms::name, nameVal); + GetAttr(kNameSpaceID_None, nsGkAtoms::id, idVal); + + SetFlags(ADDED_TO_FORM); + + mForm->AddImageElement(this); + + if (!nameVal.IsEmpty()) { + mForm->AddImageElementToTable(this, nameVal); + } + + if (!idVal.IsEmpty()) { + mForm->AddImageElementToTable(this, idVal); + } + } +} + +void HTMLImageElement::MaybeLoadImage() { // Our base URI may have changed; claim that our URI changed, and the // nsImageLoadingContent will decide whether a new image load is warranted. // Note, check LoadingEnabled() after LoadImage call. nsAutoString uri; if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, uri) && (NS_FAILED(LoadImage(uri, false, true)) || @@ -572,10 +654,60 @@ HTMLImageElement::GetCORSMode() } JSObject* HTMLImageElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aScope) { return HTMLImageElementBinding::Wrap(aCx, aScope, this); } +#ifdef DEBUG +nsIDOMHTMLFormElement* +HTMLImageElement::GetForm() const +{ + return mForm; +} +#endif + +void +HTMLImageElement::SetForm(nsIDOMHTMLFormElement* aForm) +{ + NS_PRECONDITION(aForm, "Don't pass null here"); + NS_ASSERTION(!mForm, + "We don't support switching from one non-null form to another."); + + mForm = static_cast<nsHTMLFormElement*>(aForm); +} + +void +HTMLImageElement::ClearForm(bool aRemoveFromForm) +{ + NS_ASSERTION((mForm != nullptr) == HasFlag(ADDED_TO_FORM), + "Form control should have had flag set correctly"); + + if (!mForm) { + return; + } + + if (aRemoveFromForm) { + nsAutoString nameVal, idVal; + GetAttr(kNameSpaceID_None, nsGkAtoms::name, nameVal); + GetAttr(kNameSpaceID_None, nsGkAtoms::id, idVal); + + mForm->RemoveImageElement(this); + + if (!nameVal.IsEmpty()) { + mForm->RemoveImageElementFromTable(this, nameVal, + nsHTMLFormElement::ElementRemoved); + } + + if (!idVal.IsEmpty()) { + mForm->RemoveImageElementFromTable(this, idVal, + nsHTMLFormElement::ElementRemoved); + } + } + + UnsetFlags(ADDED_TO_FORM); + mForm = nullptr; +} + } // namespace dom } // namespace mozilla
--- a/content/html/content/src/HTMLImageElement.h +++ b/content/html/content/src/HTMLImageElement.h @@ -169,20 +169,38 @@ public: int32_t X(); int32_t Y(); // Uses XPCOM GetLowsrc. void SetLowsrc(const nsAString& aLowsrc, ErrorResult& aError) { SetHTMLAttr(nsGkAtoms::lowsrc, aLowsrc, aError); } +#ifdef DEBUG + nsIDOMHTMLFormElement* GetForm() const; +#endif + void SetForm(nsIDOMHTMLFormElement* aForm); + void ClearForm(bool aRemoveFromForm); + protected: CSSIntPoint GetXY(); virtual void GetItemValueText(nsAString& text) MOZ_OVERRIDE; virtual void SetItemValueText(const nsAString& text) MOZ_OVERRIDE; virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE; + void UpdateFormOwner(); + + virtual nsresult BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, + const nsAttrValueOrString* aValue, + bool aNotify) MOZ_OVERRIDE; + + virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, + const nsAttrValue* aValue, bool aNotify) MOZ_OVERRIDE; + + // This is a weak reference that this element and the HTMLFormElement + // cooperate in maintaining. + nsHTMLFormElement* mForm; }; } // namespace dom } // namespace mozilla #endif /* mozilla_dom_HTMLImageElement_h */
--- a/content/html/content/src/nsGenericHTMLElement.cpp +++ b/content/html/content/src/nsGenericHTMLElement.cpp @@ -2174,21 +2174,23 @@ nsGenericHTMLFormElement::ClearForm(bool if (aRemoveFromForm) { nsAutoString nameVal, idVal; GetAttr(kNameSpaceID_None, nsGkAtoms::name, nameVal); GetAttr(kNameSpaceID_None, nsGkAtoms::id, idVal); mForm->RemoveElement(this, true); if (!nameVal.IsEmpty()) { - mForm->RemoveElementFromTable(this, nameVal); + mForm->RemoveElementFromTable(this, nameVal, + nsHTMLFormElement::ElementRemoved); } if (!idVal.IsEmpty()) { - mForm->RemoveElementFromTable(this, idVal); + mForm->RemoveElementFromTable(this, idVal, + nsHTMLFormElement::ElementRemoved); } } UnsetFlags(ADDED_TO_FORM); mForm = nullptr; } Element* @@ -2309,31 +2311,34 @@ nsGenericHTMLFormElement::BeforeSetAttr( nsAutoString tmp; // remove the control from the hashtable as needed if (mForm && (aName == nsGkAtoms::name || aName == nsGkAtoms::id)) { GetAttr(kNameSpaceID_None, aName, tmp); if (!tmp.IsEmpty()) { - mForm->RemoveElementFromTable(this, tmp); + mForm->RemoveElementFromTable(this, tmp, + nsHTMLFormElement::AttributeUpdated); } } if (mForm && aName == nsGkAtoms::type) { GetAttr(kNameSpaceID_None, nsGkAtoms::name, tmp); if (!tmp.IsEmpty()) { - mForm->RemoveElementFromTable(this, tmp); + mForm->RemoveElementFromTable(this, tmp, + nsHTMLFormElement::AttributeUpdated); } GetAttr(kNameSpaceID_None, nsGkAtoms::id, tmp); if (!tmp.IsEmpty()) { - mForm->RemoveElementFromTable(this, tmp); + mForm->RemoveElementFromTable(this, tmp, + nsHTMLFormElement::AttributeUpdated); } mForm->RemoveElement(this, false); // Removing the element from the form can make it not be the default // control anymore. Go ahead and notify on that change, though we might // end up readding and becoming the default control again in // AfterSetAttr.
--- a/content/html/content/src/nsGenericHTMLElement.h +++ b/content/html/content/src/nsGenericHTMLElement.h @@ -1042,25 +1042,26 @@ namespace dom { class HTMLFieldSetElement; } } #define FORM_ELEMENT_FLAG_BIT(n_) NODE_FLAG_BIT(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + (n_)) // Form element specific bits enum { - // If this flag is set on an nsGenericHTMLFormElement, that means that we have - // added ourselves to our mForm. It's possible to have a non-null mForm, but - // not have this flag set. That happens when the form is set via the content - // sink. + // If this flag is set on an nsGenericHTMLFormElement or an HTMLImageElement, + // that means that we have added ourselves to our mForm. It's possible to + // have a non-null mForm, but not have this flag set. That happens when the + // form is set via the content sink. ADDED_TO_FORM = FORM_ELEMENT_FLAG_BIT(0), - // If this flag is set on an nsGenericHTMLFormElement, that means that its form - // is in the process of being unbound from the tree, and this form element - // hasn't re-found its form in nsGenericHTMLFormElement::UnbindFromTree yet. + // If this flag is set on an nsGenericHTMLFormElement or an HTMLImageElement, + // that means that its form is in the process of being unbound from the tree, + // and this form element hasn't re-found its form in + // nsGenericHTMLFormElement::UnbindFromTree yet. MAYBE_ORPHAN_FORM_ELEMENT = FORM_ELEMENT_FLAG_BIT(1) }; // NOTE: I don't think it's possible to have the above two flags set at the // same time, so if it becomes an issue we can probably merge them into the // same bit. --bz ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + 2);
--- a/content/html/content/src/nsHTMLFormElement.cpp +++ b/content/html/content/src/nsHTMLFormElement.cpp @@ -49,16 +49,19 @@ #include "nsIConstraintValidation.h" #include "nsIDOMHTMLButtonElement.h" #include "mozilla/dom/HTMLCollectionBinding.h" #include "mozilla/dom/BindingUtils.h" #include "nsSandboxFlags.h" +// images +#include "mozilla/dom/HTMLImageElement.h" + using namespace mozilla::dom; static const int NS_FORM_CONTROL_LIST_HASHTABLE_SIZE = 16; static const uint8_t NS_FORM_AUTOCOMPLETE_ON = 1; static const uint8_t NS_FORM_AUTOCOMPLETE_OFF = 0; static const nsAttrValue::EnumTable kFormAutocompleteTable[] = { @@ -99,16 +102,18 @@ public: } virtual JSObject* NamedItem(JSContext* cx, const nsAString& name, mozilla::ErrorResult& error); virtual void GetSupportedNames(nsTArray<nsString>& aNames); nsresult AddElementToTable(nsGenericHTMLFormElement* aChild, const nsAString& aName); + nsresult AddImageElementToTable(HTMLImageElement* aChild, + const nsAString& aName); nsresult RemoveElementFromTable(nsGenericHTMLFormElement* aChild, const nsAString& aName); nsresult IndexOfControl(nsIFormControl* aControl, int32_t* aIndex); nsISupports* NamedItemInternal(const nsAString& aName, bool aFlushContent); /** @@ -236,23 +241,27 @@ nsHTMLFormElement::nsHTMLFormElement(alr mPendingSubmission(nullptr), mSubmittingRequest(nullptr), mDefaultSubmitElement(nullptr), mFirstSubmitInElements(nullptr), mFirstSubmitNotInElements(nullptr), mInvalidElementsCount(0), mEverTriedInvalidSubmit(false) { + mImageNameLookupTable.Init(NS_FORM_CONTROL_LIST_HASHTABLE_SIZE); + mPastNameLookupTable.Init(NS_FORM_CONTROL_LIST_HASHTABLE_SIZE); } nsHTMLFormElement::~nsHTMLFormElement() { if (mControls) { mControls->DropFormReference(); } + + Clear(); } nsresult nsHTMLFormElement::Init() { mControls = new nsFormControlList(this); if (!mControls) { return NS_ERROR_OUT_OF_MEMORY; @@ -285,19 +294,26 @@ ElementTraverser(const nsAString& key, n cb->NoteXPCOMChild(element); return PL_DHASH_NEXT; } NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsHTMLFormElement, nsGenericHTMLElement) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControls) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageNameLookupTable) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPastNameLookupTable) tmp->mSelectedRadioButtons.EnumerateRead(ElementTraverser, &cb); NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsHTMLFormElement, + nsGenericHTMLElement) + tmp->Clear(); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + NS_IMPL_ADDREF_INHERITED(nsHTMLFormElement, Element) NS_IMPL_RELEASE_INHERITED(nsHTMLFormElement, Element) DOMCI_NODE_DATA(HTMLFormElement, nsHTMLFormElement) // QueryInterface implementation for nsHTMLFormElement NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsHTMLFormElement) @@ -450,18 +466,19 @@ nsHTMLFormElement::BindToTree(nsIDocumen nsCOMPtr<nsIHTMLDocument> htmlDoc(do_QueryInterface(aDocument)); if (htmlDoc) { htmlDoc->AddedForm(); } return rv; } +template<typename T> static void -MarkOrphans(const nsTArray<nsGenericHTMLFormElement*>& aArray) +MarkOrphans(const nsTArray<T*>& aArray) { uint32_t length = aArray.Length(); for (uint32_t i = 0; i < length; ++i) { aArray[i]->SetFlags(MAYBE_ORPHAN_FORM_ELEMENT); } } static void @@ -478,18 +495,17 @@ CollectOrphans(nsINode* aRemovalRoot, // Walk backwards so that if we remove elements we can just keep iterating uint32_t length = aArray.Length(); for (uint32_t i = length; i > 0; --i) { nsGenericHTMLFormElement* node = aArray[i-1]; // Now if MAYBE_ORPHAN_FORM_ELEMENT is not set, that would mean that the // node is in fact a descendant of the form and hence should stay in the // form. If it _is_ set, then we need to check whether the node is a - // descendant of aRemovalRoot. If it is, we leave it in the form. See - // also the code in nsGenericHTMLFormElement::FindForm. + // descendant of aRemovalRoot. If it is, we leave it in the form. #ifdef DEBUG bool removed = false; #endif if (node->HasFlag(MAYBE_ORPHAN_FORM_ELEMENT)) { node->UnsetFlags(MAYBE_ORPHAN_FORM_ELEMENT); if (!nsContentUtils::ContentIsDescendantOf(node, aRemovalRoot)) { node->ClearForm(true); @@ -506,51 +522,97 @@ CollectOrphans(nsINode* aRemovalRoot, nsCOMPtr<nsIDOMHTMLFormElement> form; node->GetForm(getter_AddRefs(form)); NS_ASSERTION(form == aThisForm, "How did that happen?"); } #endif /* DEBUG */ } } +static void +CollectOrphans(nsINode* aRemovalRoot, + const nsTArray<HTMLImageElement*>& aArray +#ifdef DEBUG + , nsIDOMHTMLFormElement* aThisForm +#endif + ) +{ + // Walk backwards so that if we remove elements we can just keep iterating + uint32_t length = aArray.Length(); + for (uint32_t i = length; i > 0; --i) { + HTMLImageElement* node = aArray[i-1]; + + // Now if MAYBE_ORPHAN_FORM_ELEMENT is not set, that would mean that the + // node is in fact a descendant of the form and hence should stay in the + // form. If it _is_ set, then we need to check whether the node is a + // descendant of aRemovalRoot. If it is, we leave it in the form. +#ifdef DEBUG + bool removed = false; +#endif + if (node->HasFlag(MAYBE_ORPHAN_FORM_ELEMENT)) { + node->UnsetFlags(MAYBE_ORPHAN_FORM_ELEMENT); + if (!nsContentUtils::ContentIsDescendantOf(node, aRemovalRoot)) { + node->ClearForm(true); + +#ifdef DEBUG + removed = true; +#endif + } + } + +#ifdef DEBUG + if (!removed) { + nsCOMPtr<nsIDOMHTMLFormElement> form = node->GetForm(); + NS_ASSERTION(form == aThisForm, "How did that happen?"); + } +#endif /* DEBUG */ + } +} + void nsHTMLFormElement::UnbindFromTree(bool aDeep, bool aNullParent) { nsCOMPtr<nsIHTMLDocument> oldDocument = do_QueryInterface(GetCurrentDoc()); // Mark all of our controls as maybe being orphans MarkOrphans(mControls->mElements); MarkOrphans(mControls->mNotInElements); + MarkOrphans(mImageElements); nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); nsINode* ancestor = this; nsINode* cur; do { cur = ancestor->GetParentNode(); if (!cur) { break; } ancestor = cur; } while (1); CollectOrphans(ancestor, mControls->mElements #ifdef DEBUG , this -#endif +#endif ); CollectOrphans(ancestor, mControls->mNotInElements #ifdef DEBUG , this -#endif +#endif + ); + CollectOrphans(ancestor, mImageElements +#ifdef DEBUG + , this +#endif ); if (oldDocument) { oldDocument->RemovedForm(); - } + } ForgetCurrentSubmission(); } nsresult nsHTMLFormElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor) { aVisitor.mWantsWillHandleEvent = true; if (aVisitor.mEvent->originalTarget == static_cast<nsIContent*>(this)) { @@ -1029,45 +1091,44 @@ nsHTMLFormElement::GetElementAt(int32_t * @param aControl1 First control to compare. * @param aControl2 Second control to compare. * @param aForm Parent form of the controls. * @return < 0 if aControl1 is before aControl2, * > 0 if aControl1 is after aControl2, * 0 otherwise */ static inline int32_t -CompareFormControlPosition(nsGenericHTMLFormElement *aControl1, - nsGenericHTMLFormElement *aControl2, +CompareFormControlPosition(Element *aElement1, Element *aElement2, const nsIContent* aForm) { - NS_ASSERTION(aControl1 != aControl2, "Comparing a form control to itself"); + NS_ASSERTION(aElement1 != aElement2, "Comparing a form control to itself"); // If an element has a @form, we can assume it *might* be able to not have // a parent and still be in the form. - NS_ASSERTION((aControl1->HasAttr(kNameSpaceID_None, nsGkAtoms::form) || - aControl1->GetParent()) && - (aControl2->HasAttr(kNameSpaceID_None, nsGkAtoms::form) || - aControl2->GetParent()), + NS_ASSERTION((aElement1->HasAttr(kNameSpaceID_None, nsGkAtoms::form) || + aElement1->GetParent()) && + (aElement2->HasAttr(kNameSpaceID_None, nsGkAtoms::form) || + aElement2->GetParent()), "Form controls should always have parents"); // If we pass aForm, we are assuming both controls are form descendants which // is not always the case. This function should work but maybe slower. // However, checking if both elements are form descendants may be slow too... // TODO: remove the prevent asserts fix, see bug 598468. #ifdef DEBUG nsLayoutUtils::gPreventAssertInCompareTreePosition = true; - int32_t rVal = nsLayoutUtils::CompareTreePosition(aControl1, aControl2, aForm); + int32_t rVal = nsLayoutUtils::CompareTreePosition(aElement1, aElement2, aForm); nsLayoutUtils::gPreventAssertInCompareTreePosition = false; return rVal; #else // DEBUG - return nsLayoutUtils::CompareTreePosition(aControl1, aControl2, aForm); + return nsLayoutUtils::CompareTreePosition(aElement1, aElement2, aForm); #endif // DEBUG } - + #ifdef DEBUG /** * Checks that all form elements are in document order. Asserts if any pair of * consecutive elements are not in increasing document order. * * @param aControls List of form controls to check. * @param aForm Parent form of the controls. */ @@ -1101,72 +1162,84 @@ nsHTMLFormElement::PostPasswordEvent() } nsRefPtr<FormPasswordEvent> event = new FormPasswordEvent(this, NS_LITERAL_STRING("DOMFormHasPassword")); mFormPasswordEvent = event; event->PostDOMEvent(); } +// This function return true if the element, once appended, is the last one in +// the array. +template<typename ElementType> +static bool +AddElementToList(nsTArray<ElementType*>& aList, ElementType* aChild, + nsHTMLFormElement* aForm) +{ + NS_ASSERTION(aList.IndexOf(aChild) == aList.NoIndex, + "aChild already in aList"); + + uint32_t count = aList.Length(); + ElementType* element; + bool lastElement = false; + + // Optimize most common case where we insert at the end. + int32_t position = -1; + if (count > 0) { + element = aList[count - 1]; + position = CompareFormControlPosition(aChild, element, aForm); + } + + // If this item comes after the last element, or the elements array is + // empty, we append to the end. Otherwise, we do a binary search to + // determine where the element should go. + if (position >= 0 || count == 0) { + // WEAK - don't addref + aList.AppendElement(aChild); + lastElement = true; + } + else { + int32_t low = 0, mid, high; + high = count - 1; + + while (low <= high) { + mid = (low + high) / 2; + + element = aList[mid]; + position = CompareFormControlPosition(aChild, element, aForm); + if (position >= 0) + low = mid + 1; + else + high = mid - 1; + } + + // WEAK - don't addref + aList.InsertElementAt(low, aChild); + } + + return lastElement; +} + nsresult nsHTMLFormElement::AddElement(nsGenericHTMLFormElement* aChild, bool aUpdateValidity, bool aNotify) { // If an element has a @form, we can assume it *might* be able to not have // a parent and still be in the form. NS_ASSERTION(aChild->HasAttr(kNameSpaceID_None, nsGkAtoms::form) || aChild->GetParent(), "Form control should have a parent"); // Determine whether to add the new element to the elements or // the not-in-elements list. bool childInElements = ShouldBeInElements(aChild); nsTArray<nsGenericHTMLFormElement*>& controlList = childInElements ? mControls->mElements : mControls->mNotInElements; - - NS_ASSERTION(controlList.IndexOf(aChild) == controlList.NoIndex, - "Form control already in form"); - - uint32_t count = controlList.Length(); - nsGenericHTMLFormElement* element; - - // Optimize most common case where we insert at the end. - bool lastElement = false; - int32_t position = -1; - if (count > 0) { - element = controlList[count - 1]; - position = CompareFormControlPosition(aChild, element, this); - } - - // If this item comes after the last element, or the elements array is - // empty, we append to the end. Otherwise, we do a binary search to - // determine where the element should go. - if (position >= 0 || count == 0) { - // WEAK - don't addref - controlList.AppendElement(aChild); - lastElement = true; - } - else { - int32_t low = 0, mid, high; - high = count - 1; - - while (low <= high) { - mid = (low + high) / 2; - - element = controlList[mid]; - position = CompareFormControlPosition(aChild, element, this); - if (position >= 0) - low = mid + 1; - else - high = mid - 1; - } - - // WEAK - don't addref - controlList.InsertElementAt(low, aChild); - } + + bool lastElement = AddElementToList(controlList, aChild, this); #ifdef DEBUG AssertDocumentOrder(controlList, this); #endif int32_t type = aChild->GetType(); // @@ -1354,41 +1427,113 @@ nsHTMLFormElement::HandleDefaultSubmitRe "What happened here?"); // Notify about change if needed. if (mDefaultSubmitElement) { mDefaultSubmitElement->UpdateState(true); } } +static nsresult +RemoveElementFromTableInternal( + nsInterfaceHashtable<nsStringHashKey,nsISupports>& aTable, + nsIContent* aChild, const nsAString& aName) +{ + nsCOMPtr<nsISupports> supports; + + if (!aTable.Get(aName, getter_AddRefs(supports))) + return NS_OK; + + // Single element in the hash, just remove it if it's the one + // we're trying to remove... + if (supports == aChild) { + aTable.Remove(aName); + return NS_OK; + } + + nsCOMPtr<nsIContent> content(do_QueryInterface(supports)); + if (content) { + return NS_OK; + } + + nsCOMPtr<nsIDOMNodeList> nodeList(do_QueryInterface(supports)); + NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE); + + // Upcast, uggly, but it works! + nsBaseContentList *list = static_cast<nsBaseContentList*>(nodeList.get()); + + list->RemoveElement(aChild); + + uint32_t length = 0; + list->GetLength(&length); + + if (!length) { + // If the list is empty we remove if from our hash, this shouldn't + // happen tho + aTable.Remove(aName); + } else if (length == 1) { + // Only one element left, replace the list in the hash with the + // single element. + nsIContent* node = list->Item(0); + if (node) { + aTable.Put(aName, node); + } + } + + return NS_OK; +} + +static PLDHashOperator +RemovePastNames(const nsAString& aName, + nsCOMPtr<nsISupports>& aData, + void* aClosure) +{ + return aClosure == aData ? PL_DHASH_REMOVE : PL_DHASH_NEXT; +} + nsresult nsHTMLFormElement::RemoveElementFromTable(nsGenericHTMLFormElement* aElement, - const nsAString& aName) + const nsAString& aName, + RemoveElementReason aRemoveReason) { + // If the element is being removed from the form, we have to remove it from + // the past names map. + if (aRemoveReason == ElementRemoved) { + mPastNameLookupTable.Enumerate(RemovePastNames, aElement); + } + return mControls->RemoveElementFromTable(aElement, aName); } already_AddRefed<nsISupports> nsHTMLFormElement::FindNamedItem(const nsAString& aName, nsWrapperCache** aCache) { nsCOMPtr<nsISupports> result = DoResolveName(aName, true); if (result) { // FIXME Get the wrapper cache from DoResolveName. *aCache = nullptr; + AddToPastNamesMap(aName, result); return result.forget(); } - nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(GetCurrentDoc()); - if (!htmlDoc) { + result = mImageNameLookupTable.GetWeak(aName); + if (result) { *aCache = nullptr; - return nullptr; + AddToPastNamesMap(aName, result); + return result.forget(); } - return htmlDoc->ResolveName(aName, this, aCache); + result = mPastNameLookupTable.GetWeak(aName); + if (result) { + *aCache = nullptr; + return result.forget(); + } + + return nullptr; } already_AddRefed<nsISupports> nsHTMLFormElement::DoResolveName(const nsAString& aName, bool aFlushContent) { nsCOMPtr<nsISupports> result = mControls->NamedItemInternal(aName, aFlushContent); @@ -1905,17 +2050,17 @@ nsHTMLFormElement::OnStatusChange(nsIWeb NS_IMETHODIMP nsHTMLFormElement::OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest, uint32_t state) { NS_NOTREACHED("notification excluded in AddProgressListener(...)"); return NS_OK; } - + NS_IMETHODIMP_(int32_t) nsHTMLFormElement::IndexOfControl(nsIFormControl* aControl) { int32_t index = 0; return mControls->IndexOfControl(aControl, &index) == NS_OK ? index : 0; } void @@ -2136,16 +2281,27 @@ nsHTMLFormElement::IntrinsicState() cons state |= NS_EVENT_STATE_INVALID; } else { state |= NS_EVENT_STATE_VALID; } return state; } +void +nsHTMLFormElement::Clear() +{ + for (int32_t i = mImageElements.Length() - 1; i >= 0; i--) { + mImageElements[i]->ClearForm(false); + } + mImageElements.Clear(); + mImageNameLookupTable.Clear(); + mPastNameLookupTable.Clear(); +} + //---------------------------------------------------------------------- // nsFormControlList implementation, this could go away if there were // a lightweight collection implementation somewhere nsFormControlList::nsFormControlList(nsHTMLFormElement* aForm) : mForm(aForm), // Initialize the elements list to have an initial capacity // of 8 to reduce allocations on small forms. @@ -2224,17 +2380,17 @@ NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFormControlList) NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFormControlList) // nsIDOMHTMLCollection interface -NS_IMETHODIMP +NS_IMETHODIMP nsFormControlList::GetLength(uint32_t* aLength) { FlushPendingNotifications(); *aLength = mElements.Length(); return NS_OK; } NS_IMETHODIMP @@ -2293,63 +2449,60 @@ nsFormControlList::NamedItemInternal(con { if (aFlushContent) { FlushPendingNotifications(); } return mNameLookupTable.GetWeak(aName); } -nsresult -nsFormControlList::AddElementToTable(nsGenericHTMLFormElement* aChild, - const nsAString& aName) +static nsresult +AddElementToTableInternal( + nsInterfaceHashtable<nsStringHashKey,nsISupports>& aTable, + nsIContent* aChild, const nsAString& aName, nsHTMLFormElement* aForm) { - if (!ShouldBeInElements(aChild)) { - return NS_OK; - } - nsCOMPtr<nsISupports> supports; - mNameLookupTable.Get(aName, getter_AddRefs(supports)); + aTable.Get(aName, getter_AddRefs(supports)); if (!supports) { - // No entry found, add the form control - mNameLookupTable.Put(aName, NS_ISUPPORTS_CAST(nsIContent*, aChild)); + // No entry found, add the element + aTable.Put(aName, aChild); } else { // Found something in the hash, check its type nsCOMPtr<nsIContent> content = do_QueryInterface(supports); if (content) { // Check if the new content is the same as the one we found in the // hash, if it is then we leave it in the hash as it is, this will // happen if a form control has both a name and an id with the same // value if (content == aChild) { return NS_OK; } // Found an element, create a list, add the element to the list and put // the list in the hash - nsSimpleContentList *list = new nsSimpleContentList(mForm); + nsSimpleContentList *list = new nsSimpleContentList(aForm); // If an element has a @form, we can assume it *might* be able to not have // a parent and still be in the form. NS_ASSERTION(content->HasAttr(kNameSpaceID_None, nsGkAtoms::form) || content->GetParent(), "Item in list without parent"); // Determine the ordering between the new and old element. bool newFirst = nsContentUtils::PositionIsBefore(aChild, content); - list->AppendElement(newFirst ? aChild : content); - list->AppendElement(newFirst ? content : aChild); + list->AppendElement(newFirst ? aChild : content.get()); + list->AppendElement(newFirst ? content.get() : aChild); nsCOMPtr<nsISupports> listSupports = do_QueryObject(list); // Replace the element with the list. - mNameLookupTable.Put(aName, listSupports); + aTable.Put(aName, listSupports); } else { // There's already a list in the hash, add the child to the list nsCOMPtr<nsIDOMNodeList> nodeList = do_QueryInterface(supports); NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE); // Upcast, uggly, but it works! nsSimpleContentList *list = static_cast<nsSimpleContentList*>(nodeList.get()); @@ -2391,16 +2544,27 @@ nsFormControlList::AddElementToTable(nsG list->InsertElementAt(aChild, first); } } return NS_OK; } nsresult +nsFormControlList::AddElementToTable(nsGenericHTMLFormElement* aChild, + const nsAString& aName) +{ + if (!ShouldBeInElements(aChild)) { + return NS_OK; + } + + return AddElementToTableInternal(mNameLookupTable, aChild, aName, mForm); +} + +nsresult nsFormControlList::IndexOfControl(nsIFormControl* aControl, int32_t* aIndex) { // Note -- not a DOM method; callers should handle flushing themselves NS_ENSURE_ARG_POINTER(aIndex); *aIndex = mElements.IndexOf(aControl); @@ -2411,58 +2575,17 @@ nsFormControlList::IndexOfControl(nsIFor nsresult nsFormControlList::RemoveElementFromTable(nsGenericHTMLFormElement* aChild, const nsAString& aName) { if (!ShouldBeInElements(aChild)) { return NS_OK; } - nsCOMPtr<nsISupports> supports; - - if (!mNameLookupTable.Get(aName, getter_AddRefs(supports))) - return NS_OK; - - nsCOMPtr<nsIFormControl> fctrl(do_QueryInterface(supports)); - - if (fctrl) { - // Single element in the hash, just remove it if it's the one - // we're trying to remove... - if (fctrl == aChild) { - mNameLookupTable.Remove(aName); - } - - return NS_OK; - } - - nsCOMPtr<nsIDOMNodeList> nodeList(do_QueryInterface(supports)); - NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE); - - // Upcast, uggly, but it works! - nsBaseContentList *list = static_cast<nsBaseContentList*>(nodeList.get()); - - list->RemoveElement(aChild); - - uint32_t length = 0; - list->GetLength(&length); - - if (!length) { - // If the list is empty we remove if from our hash, this shouldn't - // happen tho - mNameLookupTable.Remove(aName); - } else if (length == 1) { - // Only one element left, replace the list in the hash with the - // single element. - nsIContent* node = list->Item(0); - if (node) { - mNameLookupTable.Put(aName, node); - } - } - - return NS_OK; + return RemoveElementFromTableInternal(mNameLookupTable, aChild, aName); } nsresult nsFormControlList::GetSortedControls(nsTArray<nsGenericHTMLFormElement*>& aControls) const { #ifdef DEBUG AssertDocumentOrder(mElements, mForm); AssertDocumentOrder(mNotInElements, mForm); @@ -2576,8 +2699,60 @@ void nsFormControlList::GetSupportedNames(nsTArray<nsString>& aNames) { FlushPendingNotifications(); // Just enumerate mNameLookupTable. This won't guarantee order, but // that's OK, because the HTML5 spec doesn't define an order for // this enumeration. mNameLookupTable.EnumerateRead(CollectNames, &aNames); } + +nsresult +nsHTMLFormElement::AddImageElement(HTMLImageElement* aChild) +{ + AddElementToList(mImageElements, aChild, this); + return NS_OK; +} + +nsresult +nsHTMLFormElement::AddImageElementToTable(HTMLImageElement* aChild, + const nsAString& aName) +{ + return AddElementToTableInternal(mImageNameLookupTable, aChild, aName, this); +} + +nsresult +nsHTMLFormElement::RemoveImageElement(HTMLImageElement* aChild) +{ + uint32_t index = mImageElements.IndexOf(aChild); + NS_ENSURE_STATE(index != mImageElements.NoIndex); + + mImageElements.RemoveElementAt(index); + return NS_OK; +} + +nsresult +nsHTMLFormElement::RemoveImageElementFromTable(HTMLImageElement* aElement, + const nsAString& aName, + RemoveElementReason aRemoveReason) +{ + // If the element is being removed from the form, we have to remove it from + // the past names map. + if (aRemoveReason == ElementRemoved) { + mPastNameLookupTable.Enumerate(RemovePastNames, aElement); + } + + return RemoveElementFromTableInternal(mImageNameLookupTable, aElement, aName); +} + +void +nsHTMLFormElement::AddToPastNamesMap(const nsAString& aName, + nsISupports* aChild) +{ + // If candidates contains exactly one node. Add a mapping from name to the + // node in candidates in the form element's past names map, replacing the + // previous entry with the same name, if any. + nsCOMPtr<nsIContent> node = do_QueryInterface(aChild); + if (node) { + mPastNameLookupTable.Put(aName, aChild); + } +} +
--- a/content/html/content/src/nsHTMLFormElement.h +++ b/content/html/content/src/nsHTMLFormElement.h @@ -27,16 +27,22 @@ #undef GetClassInfo #endif #endif class nsFormControlList; class nsIMutableArray; class nsIURI; +namespace mozilla { +namespace dom { +class HTMLImageElement; +} +} + class nsHTMLFormElement : public nsGenericHTMLElement, public nsIDOMHTMLFormElement, public nsIWebProgressListener, public nsIForm, public nsIRadioGroupContainer { public: nsHTMLFormElement(already_AddRefed<nsINodeInfo> aNodeInfo); @@ -116,18 +122,18 @@ public: /** * Forget all information about the current submission (and the fact that we * are currently submitting at all). */ void ForgetCurrentSubmission(); virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE; - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_NO_UNLINK(nsHTMLFormElement, - nsGenericHTMLElement) + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsHTMLFormElement, + nsGenericHTMLElement) /** * Remove an element from this form's list of elements * * @param aElement the element to remove * @param aUpdateValidity If true, updates the form validity. * @return NS_OK if the element was successfully removed. */ @@ -138,41 +144,92 @@ public: * Remove an element from the lookup table maintained by the form. * We can't fold this method into RemoveElement() because when * RemoveElement() is called it doesn't know if the element is * removed because the id attribute has changed, or bacause the * name attribute has changed. * * @param aElement the element to remove * @param aName the name or id of the element to remove + * @param aRemoveReason describe why this element is removed. If the element + * is removed because it's removed from the form, it will be removed + * from the past names map too, otherwise it will stay in the past + * names map. * @return NS_OK if the element was successfully removed. */ + enum RemoveElementReason { + AttributeUpdated, + ElementRemoved + }; nsresult RemoveElementFromTable(nsGenericHTMLFormElement* aElement, - const nsAString& aName); + const nsAString& aName, + RemoveElementReason aRemoveReason); + /** * Add an element to end of this form's list of elements * * @param aElement the element to add * @param aUpdateValidity If true, the form validity will be updated. * @param aNotify If true, send nsIDocumentObserver notifications as needed. * @return NS_OK if the element was successfully added */ nsresult AddElement(nsGenericHTMLFormElement* aElement, bool aUpdateValidity, bool aNotify); - /** + /** * Add an element to the lookup table maintained by the form. * * We can't fold this method into AddElement() because when * AddElement() is called, the form control has no * attributes. The name or id attributes of the form control * are used as a key into the table. */ nsresult AddElementToTable(nsGenericHTMLFormElement* aChild, const nsAString& aName); + + /** + * Remove an image element from this form's list of image elements + * + * @param aElement the image element to remove + * @return NS_OK if the element was successfully removed. + */ + nsresult RemoveImageElement(mozilla::dom::HTMLImageElement* aElement); + + /** + * Remove an image element from the lookup table maintained by the form. + * We can't fold this method into RemoveImageElement() because when + * RemoveImageElement() is called it doesn't know if the element is + * removed because the id attribute has changed, or because the + * name attribute has changed. + * + * @param aElement the image element to remove + * @param aName the name or id of the element to remove + * @return NS_OK if the element was successfully removed. + */ + nsresult RemoveImageElementFromTable(mozilla::dom::HTMLImageElement* aElement, + const nsAString& aName, + RemoveElementReason aRemoveReason); + /** + * Add an image element to the end of this form's list of image elements + * + * @param aElement the element to add + * @return NS_OK if the element was successfully added + */ + nsresult AddImageElement(mozilla::dom::HTMLImageElement* aElement); + + /** + * Add an image element to the lookup table maintained by the form. + * + * We can't fold this method into AddImageElement() because when + * AddImageElement() is called, the image attributes can change. + * The name or id attributes of the image are used as a key into the table. + */ + nsresult AddImageElementToTable(mozilla::dom::HTMLImageElement* aChild, + const nsAString& aName); + /** * Return whether there is one and only one input text control. * * @return Whether there is exactly one input text control. */ bool HasSingleTextControl() const; /** @@ -358,16 +415,22 @@ protected: * * @param aInvalidElements [out] parameter containing the list of unhandled * invalid controls. * * @return Whether the form is currently valid. */ bool CheckFormValidity(nsIMutableArray* aInvalidElements) const; + // Clear the mImageNameLookupTable and mImageElements. + void Clear(); + + // Insert a element into the past names map. + void AddToPastNamesMap(const nsAString& aName, nsISupports* aChild); + public: /** * Flush a possible pending submission. If there was a scripted submission * triggered by a button or image, the submission was defered. This method * forces the pending submission to be submitted. (happens when the handler * returns false or there is an action/target change in the script) */ void FlushPendingSubmission(); @@ -412,16 +475,35 @@ protected: nsGenericHTMLFormElement* mDefaultSubmitElement; /** The first submit element in mElements -- WEAK */ nsGenericHTMLFormElement* mFirstSubmitInElements; /** The first submit element in mNotInElements -- WEAK */ nsGenericHTMLFormElement* mFirstSubmitNotInElements; + // This array holds on to all HTMLImageElement(s). + // This is needed to properly clean up the bi-directional references + // (both weak and strong) between the form and its HTMLImageElements. + + nsTArray<mozilla::dom::HTMLImageElement*> mImageElements; // Holds WEAK references + + // A map from an ID or NAME attribute to the HTMLImageElement(s), this + // hash holds strong references either to the named HTMLImageElement, or + // to a list of named HTMLImageElement(s), in the case where this hash + // holds on to a list of named HTMLImageElement(s) the list has weak + // references to the HTMLImageElement. + + nsInterfaceHashtable<nsStringHashKey,nsISupports> mImageNameLookupTable; + + // A map from names to elements that were gotten by those names from this + // form in that past. See "past names map" in the HTML5 specification. + + nsInterfaceHashtable<nsStringHashKey,nsISupports> mPastNameLookupTable; + /** * Number of invalid and candidate for constraint validation elements in the * form the last time UpdateValidity has been called. * @note Should only be used by UpdateValidity() and GetValidity()! */ int32_t mInvalidElementsCount; /**
--- a/content/html/content/test/Makefile.in +++ b/content/html/content/test/Makefile.in @@ -373,16 +373,18 @@ MOCHITEST_FILES = \ test_bug839371.html \ test_element_prototype.html \ test_formData.html \ test_audio_wakelock.html \ test_video_wakelock.html \ wakelock.ogg \ wakelock.ogv \ test_bug869040.html \ + test_bug870787.html \ + test_bug879319.html \ allowMedia.sjs \ test_bug874758.html \ $(NULL) MOCHITEST_CHROME_FILES = \ test_allowMedia.html \ $(NULL)
new file mode 100644 --- /dev/null +++ b/content/html/content/test/test_bug870787.html @@ -0,0 +1,84 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=870787 +--> +<head> + <title>Test for Bug 870787</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="reflect.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=870787">Mozilla Bug 870787</a> + +<p id="msg"></p> + +<form id="form0"></form> +<img name="img0" id="img0id"> + +<img name="img1" id="img1id" /> +<form id="form1"> + <img name="img2" id="img2id" /> +</form> +<img name="img3" id="img3id" /> + +<table> + <form id="form2"> + <tr><td> + <button name="input1" id="input1id" /> + <input name="input2" id="input2id" /> + </form> +</table> + +<table> + <form id="form3"> + <tr><td> + <img name="img4" id="img4id" /> + <img name="img5" id="img5id" /> + </form> +</table> + +<form id="form4"><img id="img6"></form> + +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 870787 **/ + +var form0 = document.getElementById("form0"); +ok(form0, "Form0 exists"); +ok(!form0.img0, "Form0.img0 doesn't exist"); +ok(!form0.img0id, "Form0.img0id doesn't exist"); + +var form1 = document.getElementById("form1"); +ok(form1, "Form1 exists"); +ok(!form1.img1, "Form1.img1 doesn't exist"); +ok(!form1.img1id, "Form1.img1id doesn't exist"); +is(form1.img2, document.getElementById("img2id"), "Form1.img2 exists"); +is(form1.img2id, document.getElementById("img2id"), "Form1.img2id exists"); +ok(!form1.img3, "Form1.img3 doesn't exist"); +ok(!form1.img3id, "Form1.img3id doesn't exist"); + +var form2 = document.getElementById("form2"); +ok(form2, "Form2 exists"); +is(form2.input1, document.getElementById("input1id"), "Form2.input1 exists"); +is(form2.input1id, document.getElementById("input1id"), "Form2.input1id exists"); +is(form2.input2, document.getElementById("input2id"), "Form2.input2 exists"); +is(form2.input2id, document.getElementById("input2id"), "Form2.input2id exists"); + +var form3 = document.getElementById("form3"); +ok(form3, "Form3 exists"); +is(form3.img4, document.getElementById("img4id"), "Form3.img4 doesn't exists"); +is(form3.img4id, document.getElementById("img4id"), "Form3.img4id doesn't exists"); +is(form3.img5, document.getElementById("img5id"), "Form3.img5 doesn't exists"); +is(form3.img5id, document.getElementById("img5id"), "Form3.img5id doesn't exists"); + +var form4 = document.getElementById("form4"); +ok(form4, "Form4 exists"); +is(Object.getOwnPropertyNames(form4.elements).indexOf("img6"), -1, "Form4.elements should not contain img6"); + +</script> +</pre> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/content/html/content/test/test_bug879319.html @@ -0,0 +1,91 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=879319 +--> +<head> + <title>Test for Bug 879319</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="reflect.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=879319">Mozilla Bug 879319</a> + +<p id="msg"></p> + +<form id="form"> + <img id="img0" name="bar0" /> +</form> +<input id="input0" name="foo0" form="form" /> +<input id="input1" name="foo1" form="form" /> +<input id="input2" name="foo2" form="form" /> + +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 879319 **/ + +var input0 = document.getElementById("input0"); +ok(input0, "input0 exists"); + +var form = document.getElementById("form"); +ok(form, "form exists"); +is(form.foo0, input0, "Form.foo0 should exist"); + +ok("foo0" in form.elements, "foo0 in form.elements"); +is(input0.form, form, "input0.form is form"); + +input0.setAttribute("name", "tmp0"); +ok("tmp0" in form.elements, "tmp0 is in form.elements"); +ok(!("foo0" in form.elements), "foo0 is not in form.elements"); +is(form.tmp0, input0, "Form.tmp0 == input0"); +is(form.foo0, input0, "Form.foo0 is still here"); + +input0.setAttribute("name", "tmp1"); +ok("tmp1" in form.elements, "tmp1 is in form.elements"); +ok(!("tmp0" in form.elements), "tmp0 is not in form.elements"); +ok(!("foo0" in form.elements), "foo0 is not in form.elements"); +is(form.tmp0, input0, "Form.tmp0 == input0"); +is(form.tmp1, input0, "Form.tmp1 == input0"); +is(form.foo0, input0, "Form.foo0 is still here"); + +input0.setAttribute("form", ""); +ok(!("foo0" in form.elements), "foo0 is not in form.elements"); +todo_is(form.foo0, undefined, "Form.foo0 should not still be here"); +todo_is(form.tmp0, undefined, "Form.tmp0 should not still be here"); +todo_is(form.tmp1, undefined, "Form.tmp1 should not still be here"); + +var input1 = document.getElementById("input1"); +ok(input1, "input1 exists"); +is(form.foo1, input1, "Form.foo1 should exist"); + +ok("foo1" in form.elements, "foo1 in form.elements"); +is(input1.form, form, "input1.form is form"); + +input1.setAttribute("name", "foo0"); +ok("foo0" in form.elements, "foo0 is in form.elements"); +is(form.foo0, input1, "Form.foo0 should be input1"); +is(form.foo1, input1, "Form.foo1 should be input1"); + +var input2 = document.getElementById("input2"); +ok(input2, "input2 exists"); +is(form.foo2, input2, "Form.foo2 should exist"); +input2.parentNode.removeChild(input2); +ok(!("foo2" in form.elements), "foo2 is not in form.elements"); +todo_is(form.foo2, undefined, "Form.foo2 should not longer be there"); + +var img0 = document.getElementById("img0"); +ok(img0, "img0 exists"); +is(form.bar0, img0, "Form.bar0 should exist"); + +img0.setAttribute("name", "old_bar0"); +is(form.bar0, img0, "Form.bar0 is still here"); + +img0.parentNode.removeChild(img0); +todo_is(form.bar0, undefined, "Form.bar0 should not be here"); + +</script> +</pre> +</body> +</html>
--- a/content/html/document/src/nsHTMLDocument.cpp +++ b/content/html/document/src/nsHTMLDocument.cpp @@ -2290,49 +2290,16 @@ nsHTMLDocument::ResolveName(const nsAStr *aCache = e; return e; } *aCache = nullptr; return nullptr; } -already_AddRefed<nsISupports> -nsHTMLDocument::ResolveName(const nsAString& aName, - nsIContent *aForm, - nsWrapperCache **aCache) -{ - nsISupports* result = ResolveName(aName, aCache); - if (!result) { - return nullptr; - } - - nsCOMPtr<nsIContent> node = do_QueryInterface(result); - if (!node) { - // We create a nsFormContentList which will filter out the elements in the - // list that don't belong to aForm. - nsRefPtr<nsBaseContentList> list = - new nsFormContentList(aForm, *static_cast<nsBaseContentList*>(result)); - if (list->Length() > 1) { - *aCache = list; - return list.forget(); - } - - // After the nsFormContentList is done filtering there's either nothing or - // one element in the list. Return that element, or null if there's no - // element in the list. - node = list->Item(0); - } else if (!nsContentUtils::BelongsInForm(aForm, node)) { - node = nullptr; - } - - *aCache = node; - return node.forget(); -} - JSObject* nsHTMLDocument::NamedGetter(JSContext* cx, const nsAString& aName, bool& aFound, ErrorResult& rv) { nsWrapperCache* cache; nsISupports* supp = ResolveName(aName, &cache); if (!supp) { aFound = false;
--- a/content/html/document/src/nsHTMLDocument.h +++ b/content/html/document/src/nsHTMLDocument.h @@ -110,19 +110,16 @@ public: * id. */ nsISupports *GetDocumentAllResult(const nsAString& aID, nsWrapperCache **aCache, nsresult *aResult); JSObject* GetAll(JSContext* aCx, mozilla::ErrorResult& aRv); nsISupports* ResolveName(const nsAString& aName, nsWrapperCache **aCache); - virtual already_AddRefed<nsISupports> ResolveName(const nsAString& aName, - nsIContent *aForm, - nsWrapperCache **aCache) MOZ_OVERRIDE; virtual void AddedForm() MOZ_OVERRIDE; virtual void RemovedForm() MOZ_OVERRIDE; virtual int32_t GetNumFormsSynchronous() MOZ_OVERRIDE; virtual void TearingDownEditor(nsIEditor *aEditor) MOZ_OVERRIDE; virtual void SetIsXHTML(bool aXHTML) MOZ_OVERRIDE { mIsRegularHTML = !aXHTML; } virtual void SetDocWriteDisabled(bool aDisabled) MOZ_OVERRIDE { @@ -292,17 +289,17 @@ protected: nsRefPtr<nsContentList> mApplets; nsRefPtr<nsContentList> mEmbeds; nsRefPtr<nsContentList> mLinks; nsRefPtr<nsContentList> mAnchors; nsRefPtr<nsContentList> mScripts; nsRefPtr<nsContentList> mForms; nsRefPtr<nsContentList> mFormControls; - JSObject* mAll; + JS::Heap<JSObject*> mAll; /** # of forms in the document, synchronously set */ int32_t mNumForms; static uint32_t gWyciwygSessionCnt; static void TryHintCharset(nsIMarkupDocumentViewer* aMarkupDV, int32_t& aCharsetSource,
--- a/content/html/document/src/nsIHTMLDocument.h +++ b/content/html/document/src/nsIHTMLDocument.h @@ -28,20 +28,16 @@ class nsIHTMLDocument : public nsISuppor public: NS_DECLARE_STATIC_IID_ACCESSOR(NS_IHTMLDOCUMENT_IID) /** * Set compatibility mode for this document */ virtual void SetCompatibilityMode(nsCompatibility aMode) = 0; - virtual already_AddRefed<nsISupports> ResolveName(const nsAString& aName, - nsIContent *aForm, - nsWrapperCache **aCache) = 0; - /** * Called when form->BindToTree() is called so that document knows * immediately when a form is added */ virtual void AddedForm() = 0; /** * Called when form->SetDocument() is called so that document knows * immediately when a form is removed
--- a/content/media/MediaDecoder.cpp +++ b/content/media/MediaDecoder.cpp @@ -1041,42 +1041,16 @@ void MediaDecoder::NotifyBytesConsumed(i NS_ENSURE_TRUE_VOID(mDecoderStateMachine); MOZ_ASSERT(OnStateMachineThread() || OnDecodeThread()); if (!mIgnoreProgressData) { mDecoderPosition += aBytes; mPlaybackStatistics.AddBytes(aBytes); } } -void MediaDecoder::NextFrameUnavailableBuffering() -{ - MOZ_ASSERT(NS_IsMainThread()); - if (!mOwner || mShuttingDown || !mDecoderStateMachine) - return; - - mOwner->UpdateReadyStateForData(MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING); -} - -void MediaDecoder::NextFrameAvailable() -{ - MOZ_ASSERT(NS_IsMainThread()); - if (!mOwner || mShuttingDown || !mDecoderStateMachine) - return; - - mOwner->UpdateReadyStateForData(MediaDecoderOwner::NEXT_FRAME_AVAILABLE); -} - -void MediaDecoder::NextFrameUnavailable() -{ - MOZ_ASSERT(NS_IsMainThread()); - if (!mOwner || mShuttingDown || !mDecoderStateMachine) - return; - mOwner->UpdateReadyStateForData(MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE); -} - void MediaDecoder::UpdateReadyStateForData() { MOZ_ASSERT(NS_IsMainThread()); if (!mOwner || mShuttingDown || !mDecoderStateMachine) return; MediaDecoderOwner::NextFrameStatus frameStatus = mDecoderStateMachine->GetNextFrameStatus(); mOwner->UpdateReadyStateForData(frameStatus);
--- a/content/media/MediaDecoder.h +++ b/content/media/MediaDecoder.h @@ -705,22 +705,16 @@ public: // thread. void SeekingStarted(); // Called when the backend has changed the current playback // position. It dispatches a timeupdate event and invalidates the frame. // This must be called on the main thread only. void PlaybackPositionChanged(); - // Calls mElement->UpdateReadyStateForData, telling it which state we have - // entered. Main thread only. - void NextFrameUnavailableBuffering(); - void NextFrameAvailable(); - void NextFrameUnavailable(); - // Calls mElement->UpdateReadyStateForData, telling it whether we have // data for the next frame and if we're buffering. Main thread only. void UpdateReadyStateForData(); // Find the end of the cached data starting at the current decoder // position. int64_t GetDownloadPosition();
--- a/content/media/MediaDecoderStateMachine.cpp +++ b/content/media/MediaDecoderStateMachine.cpp @@ -2615,31 +2615,26 @@ void MediaDecoderStateMachine::UpdateRea mDecoder->GetReentrantMonitor().AssertCurrentThreadIn(); MediaDecoderOwner::NextFrameStatus nextFrameStatus = GetNextFrameStatus(); if (nextFrameStatus == mLastFrameStatus) { return; } mLastFrameStatus = nextFrameStatus; + /* This is a bit tricky. MediaDecoder::UpdateReadyStateForData will run on + * the main thread and re-evaluate GetNextFrameStatus there, passing it to + * HTMLMediaElement::UpdateReadyStateForData. It doesn't use the value of + * GetNextFrameStatus we computed here, because what we're computing here + * could be stale by the time MediaDecoder::UpdateReadyStateForData runs. + * We only compute GetNextFrameStatus here to avoid posting runnables to the main + * thread unnecessarily. + */ nsCOMPtr<nsIRunnable> event; - switch (nextFrameStatus) { - case MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING: - event = NS_NewRunnableMethod(mDecoder, &MediaDecoder::NextFrameUnavailableBuffering); - break; - case MediaDecoderOwner::NEXT_FRAME_AVAILABLE: - event = NS_NewRunnableMethod(mDecoder, &MediaDecoder::NextFrameAvailable); - break; - case MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE: - event = NS_NewRunnableMethod(mDecoder, &MediaDecoder::NextFrameUnavailable); - break; - default: - PR_NOT_REACHED("unhandled frame state"); - } - + event = NS_NewRunnableMethod(mDecoder, &MediaDecoder::UpdateReadyStateForData); NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); } bool MediaDecoderStateMachine::JustExitedQuickBuffering() { return !mDecodeStartTime.IsNull() && mQuickBuffering && (TimeStamp::Now() - mDecodeStartTime) < TimeDuration::FromMicroseconds(QUICK_BUFFER_THRESHOLD_USECS);
--- a/content/media/webaudio/AudioBuffer.cpp +++ b/content/media/webaudio/AudioBuffer.cpp @@ -74,17 +74,17 @@ AudioBuffer::InitializeBuffers(uint32_t if (!mJSChannels.SetCapacity(aNumberOfChannels)) { return false; } for (uint32_t i = 0; i < aNumberOfChannels; ++i) { JS::RootedObject array(aJSContext, JS_NewFloat32Array(aJSContext, mLength)); if (!array) { return false; } - mJSChannels.AppendElement(array); + mJSChannels.AppendElement(array.get()); } return true; } JSObject* AudioBuffer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) {
--- a/content/media/webaudio/AudioBuffer.h +++ b/content/media/webaudio/AudioBuffer.h @@ -111,17 +111,17 @@ public: void MixToMono(JSContext* aJSContext); protected: bool RestoreJSChannelData(JSContext* aJSContext); void ClearJSChannels(); nsRefPtr<AudioContext> mContext; // Float32Arrays - AutoFallibleTArray<JSObject*,2> mJSChannels; + AutoFallibleTArray<JS::Heap<JSObject*>, 2> mJSChannels; // mSharedChannels aggregates the data from mJSChannels. This is non-null // if and only if the mJSChannels are neutered. nsRefPtr<ThreadSharedFloatArrayBufferList> mSharedChannels; uint32_t mLength; float mSampleRate; };
--- a/content/media/webaudio/WaveShaperNode.h +++ b/content/media/webaudio/WaveShaperNode.h @@ -32,15 +32,15 @@ public: return mCurve; } void SetCurve(const Float32Array* aData); private: void ClearCurve(); private: - JSObject* mCurve; + JS::Heap<JSObject*> mCurve; }; } } #endif
new file mode 100644 --- /dev/null +++ b/content/xbl/src/nsXBLMaybeCompiled.h @@ -0,0 +1,145 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsXBLMaybeCompiled_h__ +#define nsXBLMaybeCompiled_h__ + +#include "js/RootingAPI.h" + +/* + * A union containing either a pointer representing uncompiled source or a + * JSObject* representing the compiled result. The class is templated on the + * source object type. + * + * The purpose of abstracting this as a separate class is to allow it to be + * wrapped in a JS::Heap<T> to correctly handle post-barriering of the JSObject + * pointer, when present. + */ +template <class UncompiledT> +class nsXBLMaybeCompiled +{ +public: + nsXBLMaybeCompiled() : mUncompiled(BIT_UNCOMPILED) {} + + nsXBLMaybeCompiled(UncompiledT* uncompiled) + : mUncompiled(reinterpret_cast<uintptr_t>(uncompiled) | BIT_UNCOMPILED) {} + + nsXBLMaybeCompiled(JSObject* compiled) : mCompiled(compiled) {} + + bool IsCompiled() const + { + return !(mUncompiled & BIT_UNCOMPILED); + } + + UncompiledT* GetUncompiled() const + { + MOZ_ASSERT(!IsCompiled(), "Attempt to get compiled function as uncompiled"); + uintptr_t unmasked = mUncompiled & ~BIT_UNCOMPILED; + return reinterpret_cast<UncompiledT*>(unmasked); + } + + JSObject* GetJSFunction() const + { + MOZ_ASSERT(IsCompiled(), "Attempt to get uncompiled function as compiled"); + return mCompiled; + } + +private: + JSObject*& UnsafeGetJSFunction() + { + MOZ_ASSERT(IsCompiled(), "Attempt to get uncompiled function as compiled"); + return mCompiled; + } + + enum { BIT_UNCOMPILED = 1 << 0 }; + + union + { + // An pointer that represents the function before being compiled, with + // BIT_UNCOMPILED set. + uintptr_t mUncompiled; + + // The JS object for the compiled result. + JSObject* mCompiled; + }; + + friend class js::RootMethods<nsXBLMaybeCompiled<UncompiledT> >; +}; + +/* Add support for JS::Heap<nsXBLMaybeCompiled>. */ +namespace js { + +template <class UncompiledT> +struct RootMethods<nsXBLMaybeCompiled<UncompiledT> > : public RootMethods<JSObject *> +{ + typedef struct RootMethods<JSObject *> Base; + + static nsXBLMaybeCompiled<UncompiledT> initial() { return nsXBLMaybeCompiled<UncompiledT>(); } + + static bool poisoned(nsXBLMaybeCompiled<UncompiledT> function) + { + return function.IsCompiled() && Base::poisoned(function.GetJSFunction()); + } + + static bool needsPostBarrier(nsXBLMaybeCompiled<UncompiledT> function) + { + return function.IsCompiled() && Base::needsPostBarrier(function.GetJSFunction()); + } + +#ifdef JSGC_GENERATIONAL + static void postBarrier(nsXBLMaybeCompiled<UncompiledT>* functionp) + { + Base::postBarrier(&functionp->UnsafeGetJSFunction()); + } + + static void relocate(nsXBLMaybeCompiled<UncompiledT>* functionp) + { + Base::relocate(&functionp->UnsafeGetJSFunction()); + } +#endif +}; + +template <class UncompiledT> +class HeapBase<nsXBLMaybeCompiled<UncompiledT> > +{ + const JS::Heap<nsXBLMaybeCompiled<UncompiledT> >& wrapper() const { + return *static_cast<const JS::Heap<nsXBLMaybeCompiled<UncompiledT> >*>(this); + } + + JS::Heap<nsXBLMaybeCompiled<UncompiledT> >& wrapper() { + return *static_cast<JS::Heap<nsXBLMaybeCompiled<UncompiledT> >*>(this); + } + + const nsXBLMaybeCompiled<UncompiledT>* extract() const { + return wrapper().address(); + } + + nsXBLMaybeCompiled<UncompiledT>* extract() { + return wrapper().unsafeGet(); + } + +public: + bool IsCompiled() const { return extract()->IsCompiled(); } + UncompiledT* GetUncompiled() const { return extract()->GetUncompiled(); } + JSObject* GetJSFunction() const { return extract()->GetJSFunction(); } + + void SetUncompiled(UncompiledT* source) { + wrapper().set(nsXBLMaybeCompiled<UncompiledT>(source)); + } + + void SetJSFunction(JSObject* function) { + wrapper().set(nsXBLMaybeCompiled<UncompiledT>(function)); + } + + JS::Heap<JSObject*>& AsHeapObject() + { + MOZ_ASSERT(extract()->IsCompiled()); + return *reinterpret_cast<JS::Heap<JSObject*>*>(this); + } +}; + +} /* namespace js */ + +#endif // nsXBLMaybeCompiled_h__
--- a/content/xbl/src/nsXBLProtoImplMethod.cpp +++ b/content/xbl/src/nsXBLProtoImplMethod.cpp @@ -19,18 +19,18 @@ #include "nsIScriptSecurityManager.h" #include "nsIXPConnect.h" #include "xpcpublic.h" #include "nsXBLPrototypeBinding.h" using namespace mozilla; nsXBLProtoImplMethod::nsXBLProtoImplMethod(const PRUnichar* aName) : - nsXBLProtoImplMember(aName), - mUncompiledMethod(BIT_UNCOMPILED) + nsXBLProtoImplMember(aName), + mMethod() { MOZ_COUNT_CTOR(nsXBLProtoImplMethod); } nsXBLProtoImplMethod::~nsXBLProtoImplMethod() { MOZ_COUNT_DTOR(nsXBLProtoImplMethod); @@ -103,22 +103,23 @@ nsXBLProtoImplMethod::InstallMember(JSCo "Should not be installing an uncompiled method"); MOZ_ASSERT(js::IsObjectInContextCompartment(aTargetClassObject, aCx)); JS::Rooted<JSObject*> globalObject(aCx, JS_GetGlobalForObject(aCx, aTargetClassObject)); JS::Rooted<JSObject*> scopeObject(aCx, xpc::GetXBLScope(aCx, globalObject)); NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY); // now we want to reevaluate our property using aContext and the script object for this window... - if (mJSMethodObject) { + JS::Rooted<JSObject*> jsMethodObject(aCx, GetCompiledMethod()); + if (jsMethodObject) { nsDependentString name(mName); // First, make the function in the compartment of the scope object. JSAutoCompartment ac(aCx, scopeObject); - JS::Rooted<JSObject*> method(aCx, ::JS_CloneFunctionObject(aCx, mJSMethodObject, scopeObject)); + JS::Rooted<JSObject*> method(aCx, ::JS_CloneFunctionObject(aCx, jsMethodObject, scopeObject)); if (!method) { return NS_ERROR_OUT_OF_MEMORY; } // Then, enter the content compartment, wrap the method pointer, and define // the wrapped version on the class object. JSAutoCompartment ac2(aCx, aTargetClassObject); if (!JS_WrapObject(aCx, method.address())) @@ -144,27 +145,27 @@ nsXBLProtoImplMethod::CompileMember(nsIS NS_PRECONDITION(aClassObject, "Must have class object to compile"); nsXBLUncompiledMethod* uncompiledMethod = GetUncompiledMethod(); // No parameters or body was supplied, so don't install method. if (!uncompiledMethod) { // Early return after which we consider ourselves compiled. - mJSMethodObject = nullptr; + SetCompiledMethod(nullptr); return NS_OK; } // Don't install method if no name was supplied. if (!mName) { delete uncompiledMethod; // Early return after which we consider ourselves compiled. - mJSMethodObject = nullptr; + SetCompiledMethod(nullptr); return NS_OK; } // We have a method. // Allocate an array for our arguments. int32_t paramCount = uncompiledMethod->GetParameterCount(); char** args = nullptr; @@ -214,73 +215,69 @@ nsXBLProtoImplMethod::CompileMember(nsIS // Destroy our uncompiled method and delete our arg list. delete uncompiledMethod; delete [] args; if (NS_FAILED(rv)) { SetUncompiledMethod(nullptr); return rv; } - mJSMethodObject = methodObject; + SetCompiledMethod(methodObject); return NS_OK; } void nsXBLProtoImplMethod::Trace(const TraceCallbacks& aCallbacks, void *aClosure) { - if (IsCompiled() && mJSMethodObject) { - aCallbacks.Trace(&mJSMethodObject, "mJSMethodObject", aClosure); + if (IsCompiled() && GetCompiledMethod()) { + aCallbacks.Trace(&mMethod.AsHeapObject(), "mMethod", aClosure); } } nsresult nsXBLProtoImplMethod::Read(nsIScriptContext* aContext, nsIObjectInputStream* aStream) { JS::Rooted<JSObject*> methodObject(aContext->GetNativeContext()); nsresult rv = XBL_DeserializeFunction(aContext, aStream, &methodObject); if (NS_FAILED(rv)) { SetUncompiledMethod(nullptr); return rv; } - mJSMethodObject = methodObject; - -#ifdef DEBUG - mIsCompiled = true; -#endif + SetCompiledMethod(methodObject); return NS_OK; } nsresult nsXBLProtoImplMethod::Write(nsIScriptContext* aContext, nsIObjectOutputStream* aStream) { - if (mJSMethodObject) { + MOZ_ASSERT(IsCompiled()); + if (GetCompiledMethod()) { nsresult rv = aStream->Write8(XBLBinding_Serialize_Method); NS_ENSURE_SUCCESS(rv, rv); rv = aStream->WriteWStringZ(mName); NS_ENSURE_SUCCESS(rv, rv); - return XBL_SerializeFunction(aContext, aStream, - JS::Handle<JSObject*>::fromMarkedLocation(&mJSMethodObject)); + return XBL_SerializeFunction(aContext, aStream, mMethod.AsHeapObject()); } return NS_OK; } nsresult nsXBLProtoImplAnonymousMethod::Execute(nsIContent* aBoundElement) { NS_PRECONDITION(IsCompiled(), "Can't execute uncompiled method"); - if (!mJSMethodObject) { + if (!GetCompiledMethod()) { // Nothing to do here return NS_OK; } // Get the script context the same way // nsXBLProtoImpl::InstallImplementation does. nsIDocument* document = aBoundElement->OwnerDoc(); @@ -322,17 +319,17 @@ nsXBLProtoImplAnonymousMethod::Execute(n JSAutoCompartment ac(cx, scopeObject); if (!JS_WrapObject(cx, thisObject.address())) return NS_ERROR_OUT_OF_MEMORY; // Clone the function object, using thisObject as the parent so "this" is in // the scope chain of the resulting function (for backwards compat to the // days when this was an event handler). - JS::Rooted<JSObject*> method(cx, ::JS_CloneFunctionObject(cx, mJSMethodObject, thisObject)); + JS::Rooted<JSObject*> method(cx, ::JS_CloneFunctionObject(cx, GetCompiledMethod(), thisObject)); if (!method) return NS_ERROR_OUT_OF_MEMORY; // Now call the method // Check whether it's OK to call the method. rv = nsContentUtils::GetSecurityManager()->CheckFunctionAccess(cx, method, thisObject); @@ -359,19 +356,19 @@ nsXBLProtoImplAnonymousMethod::Execute(n return NS_OK; } nsresult nsXBLProtoImplAnonymousMethod::Write(nsIScriptContext* aContext, nsIObjectOutputStream* aStream, XBLBindingSerializeDetails aType) { - if (mJSMethodObject) { + MOZ_ASSERT(IsCompiled()); + if (GetCompiledMethod()) { nsresult rv = aStream->Write8(aType); NS_ENSURE_SUCCESS(rv, rv); - rv = XBL_SerializeFunction(aContext, aStream, - JS::Handle<JSObject*>::fromMarkedLocation(&mJSMethodObject)); + rv = XBL_SerializeFunction(aContext, aStream, mMethod.AsHeapObject()); NS_ENSURE_SUCCESS(rv, rv); } return NS_OK; }
--- a/content/xbl/src/nsXBLProtoImplMethod.h +++ b/content/xbl/src/nsXBLProtoImplMethod.h @@ -6,16 +6,17 @@ #ifndef nsXBLProtoImplMethod_h__ #define nsXBLProtoImplMethod_h__ #include "mozilla/Attributes.h" #include "nsIAtom.h" #include "nsString.h" #include "jsapi.h" #include "nsString.h" +#include "nsXBLMaybeCompiled.h" #include "nsXBLProtoImplMember.h" #include "nsXBLSerialize.h" class nsIContent; struct nsXBLParameter { nsXBLParameter* mNext; char* mName; @@ -97,39 +98,41 @@ public: virtual void Trace(const TraceCallbacks& aCallbacks, void *aClosure) MOZ_OVERRIDE; nsresult Read(nsIScriptContext* aContext, nsIObjectInputStream* aStream); virtual nsresult Write(nsIScriptContext* aContext, nsIObjectOutputStream* aStream) MOZ_OVERRIDE; bool IsCompiled() const { - return !(mUncompiledMethod & BIT_UNCOMPILED); + return mMethod.IsCompiled(); } + void SetUncompiledMethod(nsXBLUncompiledMethod* aUncompiledMethod) { - mUncompiledMethod = uintptr_t(aUncompiledMethod) | BIT_UNCOMPILED; + mMethod.SetUncompiled(aUncompiledMethod); } + nsXBLUncompiledMethod* GetUncompiledMethod() const { - uintptr_t unmasked = mUncompiledMethod & ~BIT_UNCOMPILED; - return reinterpret_cast<nsXBLUncompiledMethod*>(unmasked); + return mMethod.GetUncompiled(); } protected: - enum { BIT_UNCOMPILED = 1 << 0 }; + void SetCompiledMethod(JSObject* aCompiledMethod) + { + mMethod.SetJSFunction(aCompiledMethod); + } - union { - uintptr_t mUncompiledMethod; // An object that represents the method before being compiled. - JSObject* mJSMethodObject; // The JS object for the method (after compilation) - }; + JSObject* GetCompiledMethod() const + { + return mMethod.GetJSFunction(); + } -#ifdef DEBUG - bool mIsCompiled; -#endif + JS::Heap<nsXBLMaybeCompiled<nsXBLUncompiledMethod> > mMethod; }; class nsXBLProtoImplAnonymousMethod : public nsXBLProtoImplMethod { public: nsXBLProtoImplAnonymousMethod() : nsXBLProtoImplMethod(EmptyString().get()) {}
--- a/content/xbl/src/nsXBLProtoImplProperty.cpp +++ b/content/xbl/src/nsXBLProtoImplProperty.cpp @@ -22,18 +22,16 @@ using namespace mozilla; nsXBLProtoImplProperty::nsXBLProtoImplProperty(const PRUnichar* aName, const PRUnichar* aGetter, const PRUnichar* aSetter, const PRUnichar* aReadOnly, uint32_t aLineNumber) : nsXBLProtoImplMember(aName), - mGetterText(nullptr), - mSetterText(nullptr), mJSAttributes(JSPROP_ENUMERATE) #ifdef DEBUG , mIsCompiled(false) #endif { MOZ_COUNT_CTOR(nsXBLProtoImplProperty); if (aReadOnly) { @@ -50,125 +48,112 @@ nsXBLProtoImplProperty::nsXBLProtoImplPr AppendSetterText(nsDependentString(aSetter)); SetSetterLineNumber(aLineNumber); } } nsXBLProtoImplProperty::nsXBLProtoImplProperty(const PRUnichar* aName, const bool aIsReadOnly) : nsXBLProtoImplMember(aName), - mGetterText(nullptr), - mSetterText(nullptr), mJSAttributes(JSPROP_ENUMERATE) #ifdef DEBUG , mIsCompiled(false) #endif { MOZ_COUNT_CTOR(nsXBLProtoImplProperty); if (aIsReadOnly) mJSAttributes |= JSPROP_READONLY; } nsXBLProtoImplProperty::~nsXBLProtoImplProperty() { MOZ_COUNT_DTOR(nsXBLProtoImplProperty); - if (!(mJSAttributes & JSPROP_GETTER)) { - delete mGetterText; + if (!mGetter.IsCompiled()) { + delete mGetter.GetUncompiled(); } - if (!(mJSAttributes & JSPROP_SETTER)) { - delete mSetterText; + if (!mSetter.IsCompiled()) { + delete mSetter.GetUncompiled(); + } +} + +void nsXBLProtoImplProperty::EnsureUncompiledText(PropertyOp& aPropertyOp) +{ + if (!aPropertyOp.GetUncompiled()) { + nsXBLTextWithLineNumber* text = new nsXBLTextWithLineNumber(); + aPropertyOp.SetUncompiled(text); } } void nsXBLProtoImplProperty::AppendGetterText(const nsAString& aText) { NS_PRECONDITION(!mIsCompiled, "Must not be compiled when accessing getter text"); - if (!mGetterText) { - mGetterText = new nsXBLTextWithLineNumber(); - if (!mGetterText) - return; - } - - mGetterText->AppendText(aText); + EnsureUncompiledText(mGetter); + mGetter.GetUncompiled()->AppendText(aText); } void nsXBLProtoImplProperty::AppendSetterText(const nsAString& aText) { NS_PRECONDITION(!mIsCompiled, "Must not be compiled when accessing setter text"); - if (!mSetterText) { - mSetterText = new nsXBLTextWithLineNumber(); - if (!mSetterText) - return; - } - - mSetterText->AppendText(aText); + EnsureUncompiledText(mSetter); + mSetter.GetUncompiled()->AppendText(aText); } void nsXBLProtoImplProperty::SetGetterLineNumber(uint32_t aLineNumber) { NS_PRECONDITION(!mIsCompiled, "Must not be compiled when accessing getter text"); - if (!mGetterText) { - mGetterText = new nsXBLTextWithLineNumber(); - if (!mGetterText) - return; - } - - mGetterText->SetLineNumber(aLineNumber); + EnsureUncompiledText(mGetter); + mGetter.GetUncompiled()->SetLineNumber(aLineNumber); } void nsXBLProtoImplProperty::SetSetterLineNumber(uint32_t aLineNumber) { NS_PRECONDITION(!mIsCompiled, "Must not be compiled when accessing setter text"); - if (!mSetterText) { - mSetterText = new nsXBLTextWithLineNumber(); - if (!mSetterText) - return; - } - - mSetterText->SetLineNumber(aLineNumber); + EnsureUncompiledText(mSetter); + mSetter.GetUncompiled()->SetLineNumber(aLineNumber); } const char* gPropertyArgs[] = { "val" }; nsresult nsXBLProtoImplProperty::InstallMember(JSContext *aCx, JS::Handle<JSObject*> aTargetClassObject) { NS_PRECONDITION(mIsCompiled, "Should not be installing an uncompiled property"); + MOZ_ASSERT(mGetter.IsCompiled() && mSetter.IsCompiled()); MOZ_ASSERT(js::IsObjectInContextCompartment(aTargetClassObject, aCx)); JS::Rooted<JSObject*> globalObject(aCx, JS_GetGlobalForObject(aCx, aTargetClassObject)); JS::Rooted<JSObject*> scopeObject(aCx, xpc::GetXBLScope(aCx, globalObject)); NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY); // now we want to reevaluate our property using aContext and the script object for this window... - if (mJSGetterObject || mJSSetterObject) { + if (mGetter.GetJSFunction() || mSetter.GetJSFunction()) { // First, enter the compartment of the scope object and clone the functions. JSAutoCompartment ac(aCx, scopeObject); JS::Rooted<JSObject*> getter(aCx, nullptr); - if (mJSGetterObject) { - if (!(getter = ::JS_CloneFunctionObject(aCx, mJSGetterObject, scopeObject))) + if (mGetter.GetJSFunction()) { + if (!(getter = ::JS_CloneFunctionObject(aCx, mGetter.GetJSFunction(), scopeObject))) return NS_ERROR_OUT_OF_MEMORY; } JS::Rooted<JSObject*> setter(aCx, nullptr); - if (mJSSetterObject) { - if (!(setter = ::JS_CloneFunctionObject(aCx, mJSSetterObject, scopeObject))) + if (mSetter.GetJSFunction()) { + if (!(setter = ::JS_CloneFunctionObject(aCx, mSetter.GetJSFunction(), scopeObject))) return NS_ERROR_OUT_OF_MEMORY; } // Now, enter the content compartment, wrap the getter/setter, and define // them on the class object. JSAutoCompartment ac2(aCx, aTargetClassObject); nsDependentString name(mName); if (!JS_WrapObject(aCx, getter.address()) || @@ -187,161 +172,164 @@ nsXBLProtoImplProperty::InstallMember(JS nsresult nsXBLProtoImplProperty::CompileMember(nsIScriptContext* aContext, const nsCString& aClassStr, JS::Handle<JSObject*> aClassObject) { NS_PRECONDITION(!mIsCompiled, "Trying to compile an already-compiled property"); NS_PRECONDITION(aClassObject, "Must have class object to compile"); + MOZ_ASSERT(!mGetter.IsCompiled() && !mSetter.IsCompiled()); if (!mName) return NS_ERROR_FAILURE; // Without a valid name, we can't install the member. // We have a property. nsresult rv = NS_OK; nsAutoCString functionUri; - if (mGetterText || mSetterText) { + if (mGetter.GetUncompiled() || mSetter.GetUncompiled()) { functionUri = aClassStr; int32_t hash = functionUri.RFindChar('#'); if (hash != kNotFound) { functionUri.Truncate(hash); } } bool deletedGetter = false; - if (mGetterText && mGetterText->GetText()) { - nsDependentString getter(mGetterText->GetText()); + nsXBLTextWithLineNumber *getterText = mGetter.GetUncompiled(); + if (getterText && getterText->GetText()) { + nsDependentString getter(getterText->GetText()); if (!getter.IsEmpty()) { AutoPushJSContext cx(aContext->GetNativeContext()); JSAutoCompartment ac(cx, aClassObject); JS::CompileOptions options(cx); - options.setFileAndLine(functionUri.get(), mGetterText->GetLineNumber()) + options.setFileAndLine(functionUri.get(), getterText->GetLineNumber()) .setVersion(JSVERSION_LATEST); nsCString name = NS_LITERAL_CSTRING("get_") + NS_ConvertUTF16toUTF8(mName); JS::RootedObject rootedNull(cx, nullptr); // See bug 781070. JS::RootedObject getterObject(cx); rv = nsJSUtils::CompileFunction(cx, rootedNull, options, name, 0, nullptr, getter, getterObject.address()); - // Make sure we free mGetterText here before setting mJSGetterObject, since - // that'll overwrite mGetterText - delete mGetterText; + delete getterText; deletedGetter = true; - mJSGetterObject = getterObject; + + mGetter.SetJSFunction(getterObject); - if (mJSGetterObject && NS_SUCCEEDED(rv)) { + if (mGetter.GetJSFunction() && NS_SUCCEEDED(rv)) { mJSAttributes |= JSPROP_GETTER | JSPROP_SHARED; } if (NS_FAILED(rv)) { - mJSGetterObject = nullptr; + mGetter.SetJSFunction(nullptr); mJSAttributes &= ~JSPROP_GETTER; /*chaining to return failure*/ } } } // if getter is not empty if (!deletedGetter) { // Empty getter - delete mGetterText; - mJSGetterObject = nullptr; + delete getterText; + mGetter.SetJSFunction(nullptr); } if (NS_FAILED(rv)) { // We failed to compile our getter. So either we've set it to null, or // it's still set to the text object. In either case, it's safe to return // the error here, since then we'll be cleaned up as uncompiled and that // will be ok. Going on and compiling the setter and _then_ returning an // error, on the other hand, will try to clean up a compiled setter as // uncompiled and crash. return rv; } bool deletedSetter = false; - if (mSetterText && mSetterText->GetText()) { - nsDependentString setter(mSetterText->GetText()); + nsXBLTextWithLineNumber *setterText = mSetter.GetUncompiled(); + if (setterText && setterText->GetText()) { + nsDependentString setter(setterText->GetText()); if (!setter.IsEmpty()) { AutoPushJSContext cx(aContext->GetNativeContext()); JSAutoCompartment ac(cx, aClassObject); JS::CompileOptions options(cx); - options.setFileAndLine(functionUri.get(), mSetterText->GetLineNumber()) + options.setFileAndLine(functionUri.get(), setterText->GetLineNumber()) .setVersion(JSVERSION_LATEST); nsCString name = NS_LITERAL_CSTRING("set_") + NS_ConvertUTF16toUTF8(mName); JS::RootedObject rootedNull(cx, nullptr); // See bug 781070. JS::RootedObject setterObject(cx); rv = nsJSUtils::CompileFunction(cx, rootedNull, options, name, 1, gPropertyArgs, setter, setterObject.address()); - // Make sure we free mSetterText here before setting mJSGetterObject, since - // that'll overwrite mSetterText - delete mSetterText; + delete setterText; deletedSetter = true; - mJSSetterObject = setterObject; + mSetter.SetJSFunction(setterObject); - if (mJSSetterObject && NS_SUCCEEDED(rv)) { + if (mSetter.GetJSFunction() && NS_SUCCEEDED(rv)) { mJSAttributes |= JSPROP_SETTER | JSPROP_SHARED; } if (NS_FAILED(rv)) { - mJSSetterObject = nullptr; + mSetter.SetJSFunction(nullptr); mJSAttributes &= ~JSPROP_SETTER; /*chaining to return failure*/ } } } // if setter wasn't empty.... if (!deletedSetter) { // Empty setter - delete mSetterText; - mJSSetterObject = nullptr; + delete setterText; + mSetter.SetJSFunction(nullptr); } #ifdef DEBUG mIsCompiled = NS_SUCCEEDED(rv); #endif return rv; } void nsXBLProtoImplProperty::Trace(const TraceCallbacks& aCallbacks, void *aClosure) { if (mJSAttributes & JSPROP_GETTER) { - aCallbacks.Trace(&mJSGetterObject, "mJSGetterObject", aClosure); + aCallbacks.Trace(&mGetter.AsHeapObject(), "mGetter", aClosure); } if (mJSAttributes & JSPROP_SETTER) { - aCallbacks.Trace(&mJSSetterObject, "mJSSetterObject", aClosure); + aCallbacks.Trace(&mSetter.AsHeapObject(), "mSetter", aClosure); } } nsresult nsXBLProtoImplProperty::Read(nsIScriptContext* aContext, nsIObjectInputStream* aStream, XBLBindingSerializeDetails aType) { + MOZ_ASSERT(!mIsCompiled); + MOZ_ASSERT(!mGetter.GetUncompiled() && !mSetter.GetUncompiled()); + JSContext *cx = aContext->GetNativeContext(); + JS::Rooted<JSObject*> getterObject(cx); if (aType == XBLBinding_Serialize_GetterProperty || aType == XBLBinding_Serialize_GetterSetterProperty) { - JS::Rooted<JSObject*> getterObject(cx); nsresult rv = XBL_DeserializeFunction(aContext, aStream, &getterObject); NS_ENSURE_SUCCESS(rv, rv); - mJSGetterObject = getterObject; mJSAttributes |= JSPROP_GETTER | JSPROP_SHARED; } - + mGetter.SetJSFunction(getterObject); + + JS::Rooted<JSObject*> setterObject(cx); if (aType == XBLBinding_Serialize_SetterProperty || aType == XBLBinding_Serialize_GetterSetterProperty) { - JS::Rooted<JSObject*> setterObject(cx); nsresult rv = XBL_DeserializeFunction(aContext, aStream, &setterObject); NS_ENSURE_SUCCESS(rv, rv); - mJSSetterObject = setterObject; mJSAttributes |= JSPROP_SETTER | JSPROP_SHARED; } + mSetter.SetJSFunction(setterObject); #ifdef DEBUG mIsCompiled = true; #endif return NS_OK; } @@ -365,21 +353,19 @@ nsXBLProtoImplProperty::Write(nsIScriptC } nsresult rv = aStream->Write8(type); NS_ENSURE_SUCCESS(rv, rv); rv = aStream->WriteWStringZ(mName); NS_ENSURE_SUCCESS(rv, rv); if (mJSAttributes & JSPROP_GETTER) { - rv = XBL_SerializeFunction(aContext, aStream, - JS::Handle<JSObject*>::fromMarkedLocation(&mJSGetterObject)); + rv = XBL_SerializeFunction(aContext, aStream, mGetter.AsHeapObject()); NS_ENSURE_SUCCESS(rv, rv); } if (mJSAttributes & JSPROP_SETTER) { - rv = XBL_SerializeFunction(aContext, aStream, - JS::Handle<JSObject*>::fromMarkedLocation(&mJSSetterObject)); + rv = XBL_SerializeFunction(aContext, aStream, mSetter.AsHeapObject()); NS_ENSURE_SUCCESS(rv, rv); } return NS_OK; }
--- a/content/xbl/src/nsXBLProtoImplProperty.h +++ b/content/xbl/src/nsXBLProtoImplProperty.h @@ -7,16 +7,17 @@ #define nsXBLProtoImplProperty_h__ #include "mozilla/Attributes.h" #include "nsIAtom.h" #include "nsString.h" #include "jsapi.h" #include "nsString.h" #include "nsXBLSerialize.h" +#include "nsXBLMaybeCompiled.h" #include "nsXBLProtoImplMember.h" class nsXBLProtoImplProperty: public nsXBLProtoImplMember { public: nsXBLProtoImplProperty(const PRUnichar* aName, const PRUnichar* aGetter, const PRUnichar* aSetter, @@ -43,30 +44,26 @@ public: nsresult Read(nsIScriptContext* aContext, nsIObjectInputStream* aStream, XBLBindingSerializeDetails aType); virtual nsresult Write(nsIScriptContext* aContext, nsIObjectOutputStream* aStream) MOZ_OVERRIDE; protected: - union { - // The raw text for the getter (prior to compilation). - nsXBLTextWithLineNumber* mGetterText; - // The JS object for the getter (after compilation) - JSObject * mJSGetterObject; - }; + typedef JS::Heap<nsXBLMaybeCompiled<nsXBLTextWithLineNumber> > PropertyOp; + + void EnsureUncompiledText(PropertyOp& aPropertyOp); - union { - // The raw text for the setter (prior to compilation). - nsXBLTextWithLineNumber* mSetterText; - // The JS object for the setter (after compilation) - JSObject * mJSSetterObject; - }; + // The raw text for the getter, or the JS object (after compilation). + PropertyOp mGetter; + + // The raw text for the setter, or the JS object (after compilation). + PropertyOp mSetter; - unsigned mJSAttributes; // A flag for all our JS properties (getter/setter/readonly/shared/enum) + unsigned mJSAttributes; // A flag for all our JS properties (getter/setter/readonly/shared/enum) #ifdef DEBUG bool mIsCompiled; #endif }; #endif // nsXBLProtoImplProperty_h__
--- a/content/xul/content/src/nsXULElement.cpp +++ b/content/xul/content/src/nsXULElement.cpp @@ -2378,18 +2378,17 @@ nsXULPrototypeScript::Serialize(nsIObjec // Write basic prototype data nsresult rv; rv = aStream->Write32(mLineNo); if (NS_FAILED(rv)) return rv; rv = aStream->Write32(mLangVersion); if (NS_FAILED(rv)) return rv; // And delegate the writing to the nsIScriptContext - rv = context->Serialize(aStream, - JS::Handle<JSScript*>::fromMarkedLocation(&mScriptObject)); + rv = context->Serialize(aStream, mScriptObject); if (NS_FAILED(rv)) return rv; return NS_OK; } nsresult nsXULPrototypeScript::SerializeOutOfLine(nsIObjectOutputStream* aStream, nsIScriptGlobalObject* aGlobal)
--- a/content/xul/content/src/nsXULElement.h +++ b/content/xul/content/src/nsXULElement.h @@ -238,23 +238,23 @@ public: void Set(JSScript* aObject); // It's safe to return a handle because we trace mScriptObject, no one ever // uses the handle (or the script object) past the point at which the // nsXULPrototypeScript dies, and we can't get memmoved so the // &mScriptObject pointer can't go stale. JS::Handle<JSScript*> GetScriptObject() { - return JS::Handle<JSScript*>::fromMarkedLocation(&mScriptObject); + return JS::Handle<JSScript*>(mScriptObject); } void TraceScriptObject(JSTracer* aTrc) { if (mScriptObject) { - JS_CallScriptTracer(aTrc, &mScriptObject, "active window XUL prototype script"); + JS_CallHeapScriptTracer(aTrc, &mScriptObject, "active window XUL prototype script"); } } void Trace(const TraceCallbacks& aCallbacks, void* aClosure) { if (mScriptObject) { aCallbacks.Trace(&mScriptObject, "mScriptObject", aClosure); } @@ -262,17 +262,17 @@ public: nsCOMPtr<nsIURI> mSrcURI; uint32_t mLineNo; bool mSrcLoading; bool mOutOfLine; mozilla::dom::XULDocument* mSrcLoadWaiters; // [OWNER] but not COMPtr uint32_t mLangVersion; private: - JSScript* mScriptObject; + JS::Heap<JSScript*> mScriptObject; }; class nsXULPrototypeText : public nsXULPrototypeNode { public: nsXULPrototypeText() : nsXULPrototypeNode(eType_Text) {
--- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -7885,27 +7885,16 @@ nsDocShell::RestoreFromHistory() newVM = nullptr; // Simulate the completion of the load. nsDocShell::FinishRestore(); // Restart plugins, and paint the content. if (shell) { shell->Thaw(); - - newVM = shell->GetViewManager(); - if (newVM) { - // When we insert the root view above the resulting invalidate is - // dropped because painting is suppressed in the presshell until we - // call Thaw. So we issue the invalidate here. - newRootView = newVM->GetRootView(); - if (newRootView) { - newVM->InvalidateView(newRootView); - } - } } return privWin->FireDelayedDOMEvents(); } NS_IMETHODIMP nsDocShell::CreateContentViewer(const char *aContentType, nsIRequest * request,
--- a/dom/base/DOMRequest.h +++ b/dom/base/DOMRequest.h @@ -17,17 +17,17 @@ namespace mozilla { namespace dom { class DOMRequest : public nsDOMEventTargetHelper, public nsIDOMDOMRequest { protected: - JS::Value mResult; + JS::Heap<JS::Value> mResult; nsRefPtr<DOMError> mError; bool mDone; bool mRooted; public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIDOMDOMREQUEST NS_REALLY_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetHelper)
--- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -89,17 +89,17 @@ CPP_SOURCES += [ 'nsMimeTypeArray.cpp', 'nsPerformance.cpp', 'nsPluginArray.cpp', 'nsQueryContentEventResult.cpp', 'nsScreen.cpp', 'nsScriptNameSpaceManager.cpp', 'nsStructuredCloneContainer.cpp', 'nsWindowMemoryReporter.cpp', - 'nsWindowRoot.cpp', + 'nsWindowRoot.cpp' ] EXTRA_COMPONENTS += [ 'ConsoleAPI.js', 'ConsoleAPI.manifest', 'SiteSpecificUserAgent.js', 'SiteSpecificUserAgent.manifest', ]
--- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -1583,26 +1583,26 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION( END_OUTER_WINDOW_ONLY NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindow) NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindow) static PLDHashOperator -MarkXBLHandlers(nsXBLPrototypeHandler* aKey, JSObject* aData, void* aClosure) +MarkXBLHandlers(nsXBLPrototypeHandler* aKey, JS::Heap<JSObject*>& aData, void* aClosure) { xpc_UnmarkGrayObject(aData); return PL_DHASH_NEXT; } NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindow) if (tmp->IsBlackForCC()) { if (tmp->mCachedXBLPrototypeHandlers.IsInitialized()) { - tmp->mCachedXBLPrototypeHandlers.EnumerateRead(MarkXBLHandlers, nullptr); + tmp->mCachedXBLPrototypeHandlers.Enumerate(MarkXBLHandlers, nullptr); } nsEventListenerManager* elm = tmp->GetListenerManager(false); if (elm) { elm->MarkForCC(); } tmp->UnmarkGrayTimers(); return true; } @@ -1744,17 +1744,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END struct TraceData { const TraceCallbacks& callbacks; void* closure; }; static PLDHashOperator -TraceXBLHandlers(nsXBLPrototypeHandler* aKey, JSObject*& aData, void* aClosure) +TraceXBLHandlers(nsXBLPrototypeHandler* aKey, JS::Heap<JSObject*>& aData, void* aClosure) { TraceData* data = static_cast<TraceData*>(aClosure); data->callbacks.Trace(&aData, "Cached XBL prototype handler", data->closure); return PL_DHASH_NEXT; } NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindow) if (tmp->mCachedXBLPrototypeHandlers.IsInitialized()) {
--- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -10,16 +10,17 @@ #include "mozilla/XPCOM.h" // for TimeStamp/TimeDuration // Local Includes // Helper Classes #include "nsCOMPtr.h" #include "nsAutoPtr.h" #include "nsWeakReference.h" #include "nsDataHashtable.h" +#include "nsJSThingHashtable.h" #include "nsCycleCollectionParticipant.h" // Interfaces Needed #include "nsDOMWindowList.h" #include "nsIBaseWindow.h" #include "nsIBrowserDOMWindow.h" #include "nsIDocShellTreeOwner.h" #include "nsIDocShellTreeItem.h" @@ -1229,17 +1230,17 @@ protected: bool mSetOpenerWindowCalled; nsCOMPtr<nsIURI> mLastOpenedURI; #endif bool mCleanedUp, mCallCleanUpAfterModalDialogCloses; nsCOMPtr<nsIDOMOfflineResourceList> mApplicationCache; - nsDataHashtable<nsPtrHashKey<nsXBLPrototypeHandler>, JSObject*> mCachedXBLPrototypeHandlers; + nsJSThingHashtable<nsPtrHashKey<nsXBLPrototypeHandler>, JSObject*> mCachedXBLPrototypeHandlers; nsCOMPtr<nsIDocument> mSuspendedDoc; nsRefPtr<mozilla::dom::indexedDB::IDBFactory> mIndexedDB; // This counts the number of windows that have been opened in rapid succession // (i.e. within dom.successive_dialog_time_limit of each other). It is reset // to 0 once a dialog is opened after dom.successive_dialog_time_limit seconds
--- a/dom/base/nsIJSEventListener.h +++ b/dom/base/nsIJSEventListener.h @@ -263,17 +263,17 @@ protected: NS_ASSERTION(!mTarget, "Should have called Disconnect()!"); } // Update our mScopeObject; we have to make sure we properly handle // the hold/drop stuff, so have to do it in nsJSEventListener. virtual void UpdateScopeObject(JS::Handle<JSObject*> aScopeObject) = 0; nsCOMPtr<nsIScriptContext> mContext; - JSObject* mScopeObject; + JS::Heap<JSObject*> mScopeObject; nsISupports* mTarget; nsCOMPtr<nsIAtom> mEventName; nsEventHandler mHandler; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsIJSEventListener, NS_IJSEVENTLISTENER_IID) /* factory function. aHandler must already be bound to aTarget.
--- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -3518,30 +3518,31 @@ public: // nsIJSArgArray nsresult GetArgs(uint32_t *argc, void **argv); void ReleaseJSObjects(); protected: JSContext *mContext; - JS::Value *mArgv; + JS::Heap<JS::Value> *mArgv; uint32_t mArgc; }; nsJSArgArray::nsJSArgArray(JSContext *aContext, uint32_t argc, JS::Value *argv, nsresult *prv) : mContext(aContext), mArgv(nullptr), mArgc(argc) { // copy the array - we don't know its lifetime, and ours is tied to xpcom - // refcounting. Alloc zero'd array so cleanup etc is safe. + // refcounting. if (argc) { - mArgv = (JS::Value *) PR_CALLOC(argc * sizeof(JS::Value)); + static const fallible_t fallible = fallible_t(); + mArgv = new (fallible) JS::Heap<JS::Value>[argc]; if (!mArgv) { *prv = NS_ERROR_OUT_OF_MEMORY; return; } } // Callers are allowed to pass in a null argv even for argc > 0. They can // then use GetArgs to initialize the values. @@ -3561,17 +3562,17 @@ nsJSArgArray::~nsJSArgArray() { ReleaseJSObjects(); } void nsJSArgArray::ReleaseJSObjects() { if (mArgv) { - PR_DELETE(mArgv); + delete [] mArgv; } if (mArgc > 0) { mArgc = 0; NS_DROP_JS_OBJECTS(this, nsJSArgArray); } } // QueryInterface implementation for nsJSArgArray
--- a/dom/base/nsJSTimeoutHandler.cpp +++ b/dom/base/nsJSTimeoutHandler.cpp @@ -62,20 +62,21 @@ public: void ReleaseJSObjects(); private: // filename, line number and JS language version string of the // caller of setTimeout() nsCString mFileName; uint32_t mLineNo; - nsTArray<JS::Value> mArgs; + nsTArray<JS::Heap<JS::Value> > mArgs; // The JS expression to evaluate or function to call, if !mExpr - JSFlatString *mExpr; + // Note this is always a flat string. + JS::Heap<JSString*> mExpr; nsRefPtr<Function> mFunction; }; // nsJSScriptTimeoutHandler // QueryInterface implementation for nsJSScriptTimeoutHandler NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSScriptTimeoutHandler) tmp->ReleaseJSObjects(); @@ -276,17 +277,17 @@ nsJSScriptTimeoutHandler::Init(nsGlobalW // Note: Our only caller knows to turn NS_ERROR_DOM_TYPE_ERR into NS_OK. return NS_ERROR_DOM_TYPE_ERR; } } } // if there's no document, we don't have to do anything. NS_HOLD_JS_OBJECTS(this, nsJSScriptTimeoutHandler); - mExpr = expr; + mExpr = JS_FORGET_STRING_FLATNESS(expr); // Get the calling location. const char *filename; if (nsJSUtils::GetCallingLocation(cx, &filename, &mLineNo)) { mFileName.Assign(filename); } } else if (funobj) { NS_HOLD_JS_OBJECTS(this, nsJSScriptTimeoutHandler); @@ -294,17 +295,18 @@ nsJSScriptTimeoutHandler::Init(nsGlobalW mFunction = new Function(funobj); // Create our arg array. argc is the number of arguments passed // to setTimeout or setInterval; the first two are our callback // and the delay, so only arguments after that need to go in our // array. // std::max(argc - 2, 0) wouldn't work right because argc is unsigned. uint32_t argCount = std::max(argc, 2u) - 2; - FallibleTArray<JS::Value> args; + + FallibleTArray<JS::Heap<JS::Value> > args; if (!args.SetCapacity(argCount)) { // No need to drop here, since we already have a non-null mFunction return NS_ERROR_OUT_OF_MEMORY; } for (uint32_t idx = 0; idx < argCount; ++idx) { *args.AppendElement() = argv[idx + 2]; } args.SwapElements(mArgs); @@ -314,17 +316,17 @@ nsJSScriptTimeoutHandler::Init(nsGlobalW *aInterval = interval; return NS_OK; } const PRUnichar * nsJSScriptTimeoutHandler::GetHandlerText() { NS_ASSERTION(mExpr, "No expression, so no handler text!"); - return ::JS_GetFlatStringChars(mExpr); + return ::JS_GetFlatStringChars(JS_ASSERT_STRING_IS_FLAT(mExpr)); } nsresult NS_CreateJSTimeoutHandler(nsGlobalWindow *aWindow, bool *aIsInterval, int32_t *aInterval, nsIScriptTimeoutHandler **aRet) { *aRet = nullptr;
--- a/dom/base/nsWrapperCache.h +++ b/dom/base/nsWrapperCache.h @@ -3,17 +3,17 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef nsWrapperCache_h___ #define nsWrapperCache_h___ #include "nsCycleCollectionParticipant.h" #include "mozilla/Assertions.h" -#include "js/RootingAPI.h" +#include "js/Value.h" struct JSTracer; class JSObject; struct JSContext; class XPCWrappedNativeScope; namespace mozilla { namespace dom { @@ -177,17 +177,17 @@ public: else { UnsetWrapperFlags(WRAPPER_BIT_PRESERVED); } } void TraceWrapper(const TraceCallbacks& aCallbacks, void* aClosure) { if (PreservingWrapper() && mWrapper) { - aCallbacks.Trace(&mWrapper, "Preserved wrapper", aClosure); + aCallbacks.Trace(&mWrapper, "Preserved wrapper", aClosure); } } /* * The following methods for getting and manipulating flags allow the unused * bits of mFlags to be used by derived classes. */ @@ -275,18 +275,18 @@ private: * (regular JS object or proxy) that has a system only wrapper for same-origin * access. */ enum { WRAPPER_HAS_SOW = 1 << 2 }; enum { kWrapperFlagsMask = (WRAPPER_BIT_PRESERVED | WRAPPER_IS_DOM_BINDING | WRAPPER_HAS_SOW) }; - JSObject* mWrapper; - uint32_t mFlags; + JS::Heap<JSObject*> mWrapper; + uint32_t mFlags; }; enum { WRAPPER_CACHE_FLAGS_BITS_USED = 3 }; NS_DEFINE_STATIC_IID_ACCESSOR(nsWrapperCache, NS_WRAPPERCACHE_IID) #define NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY \ if ( aIID.Equals(NS_GET_IID(nsWrapperCache)) ) { \
--- a/dom/base/nsWrapperCacheInlines.h +++ b/dom/base/nsWrapperCacheInlines.h @@ -45,12 +45,12 @@ nsWrapperCache::IsBlackAndDoesNotNeedTra return !hasGrayObjects; } return false; } inline void nsWrapperCache::TraceWrapperJSObject(JSTracer* aTrc, const char* aName) { - JS_CallObjectTracer(aTrc, &mWrapper, aName); + JS_CallHeapObjectTracer(aTrc, &mWrapper, aName); } #endif /* nsWrapperCache_h___ */
--- a/dom/bindings/CallbackObject.h +++ b/dom/bindings/CallbackObject.h @@ -71,17 +71,17 @@ public: * This should only be called if you are certain that the return value won't * be passed into a JS API function and that it won't be stored without being * rooted (or otherwise signaling the stored value to the CC). * * This can return a handle because we trace our mCallback. */ JS::Handle<JSObject*> CallbackPreserveColor() const { - return JS::Handle<JSObject*>::fromMarkedLocation(&mCallback); + return mCallback; } enum ExceptionHandling { eReportExceptions, eRethrowExceptions }; protected: @@ -106,17 +106,17 @@ protected: { if (mCallback) { mCallback = nullptr; NS_DROP_JS_OBJECTS(this, CallbackObject); nsLayoutStatics::Release(); } } - JSObject* mCallback; + JS::Heap<JSObject*> mCallback; class MOZ_STACK_CLASS CallSetup { /** * A class that performs whatever setup we need to safely make a * call while this class is on the stack, After the constructor * returns, the call is safe to make if GetContext() returns * non-null.
--- a/dom/bluetooth/BluetoothAdapter.cpp +++ b/dom/bluetooth/BluetoothAdapter.cpp @@ -257,39 +257,43 @@ BluetoothAdapter::SetPropertyByValue(con mClass = value.get_uint32_t(); } else if (name.EqualsLiteral("UUIDs")) { mUuids = value.get_ArrayOfnsString(); nsresult rv; nsIScriptContext* sc = GetContextForEventHandlers(&rv); NS_ENSURE_SUCCESS_VOID(rv); AutoPushJSContext cx(sc->GetNativeContext()); - if (NS_FAILED(nsTArrayToJSArray(cx, mUuids, &mJsUuids))) { + JS::Rooted<JSObject*> uuids(cx); + if (NS_FAILED(nsTArrayToJSArray(cx, mUuids, uuids.address()))) { NS_WARNING("Cannot set JS UUIDs object!"); return; } + mJsUuids = uuids; Root(); } else if (name.EqualsLiteral("Devices")) { mDeviceAddresses = value.get_ArrayOfnsString(); uint32_t length = mDeviceAddresses.Length(); for (int i = 0; i < length; i++) { mDeviceAddresses[i] = GetAddressFromObjectPath(mDeviceAddresses[i]); } nsresult rv; nsIScriptContext* sc = GetContextForEventHandlers(&rv); NS_ENSURE_SUCCESS_VOID(rv); AutoPushJSContext cx(sc->GetNativeContext()); + JS::Rooted<JSObject*> deviceAddresses(cx); if (NS_FAILED(nsTArrayToJSArray(cx, mDeviceAddresses, - &mJsDeviceAddresses))) { + deviceAddresses.address()))) { NS_WARNING("Cannot set JS Devices object!"); return; } + mJsDeviceAddresses = deviceAddresses; Root(); } else { #ifdef DEBUG nsCString warningMsg; warningMsg.AssignLiteral("Not handling adapter property: "); warningMsg.Append(NS_ConvertUTF16toUTF8(name)); NS_WARNING(warningMsg.get()); #endif
--- a/dom/bluetooth/BluetoothAdapter.h +++ b/dom/bluetooth/BluetoothAdapter.h @@ -67,16 +67,16 @@ private: bool mDiscovering; bool mPairable; bool mPowered; uint32_t mPairableTimeout; uint32_t mDiscoverableTimeout; uint32_t mClass; nsTArray<nsString> mDeviceAddresses; nsTArray<nsString> mUuids; - JSObject* mJsUuids; - JSObject* mJsDeviceAddresses; + JS::Heap<JSObject*> mJsUuids; + JS::Heap<JSObject*> mJsDeviceAddresses; bool mIsRooted; }; END_BLUETOOTH_NAMESPACE #endif
--- a/dom/bluetooth/BluetoothDevice.cpp +++ b/dom/bluetooth/BluetoothDevice.cpp @@ -119,33 +119,37 @@ BluetoothDevice::SetPropertyByValue(cons } else if (name.EqualsLiteral("UUIDs")) { mUuids = value.get_ArrayOfnsString(); nsresult rv; nsIScriptContext* sc = GetContextForEventHandlers(&rv); NS_ENSURE_SUCCESS_VOID(rv); AutoPushJSContext cx(sc->GetNativeContext()); - if (NS_FAILED(nsTArrayToJSArray(cx, mUuids, &mJsUuids))) { + JS::Rooted<JSObject*> uuids(cx); + if (NS_FAILED(nsTArrayToJSArray(cx, mUuids, uuids.address()))) { NS_WARNING("Cannot set JS UUIDs object!"); return; } + mJsUuids = uuids; Root(); } else if (name.EqualsLiteral("Services")) { mServices = value.get_ArrayOfnsString(); nsresult rv; nsIScriptContext* sc = GetContextForEventHandlers(&rv); NS_ENSURE_SUCCESS_VOID(rv); AutoPushJSContext cx(sc->GetNativeContext()); - if (NS_FAILED(nsTArrayToJSArray(cx, mServices, &mJsServices))) { + JS::Rooted<JSObject*> services(cx); + if (NS_FAILED(nsTArrayToJSArray(cx, mServices, services.address()))) { NS_WARNING("Cannot set JS Services object!"); return; } + mJsServices = services; Root(); } else { nsCString warningMsg; warningMsg.AssignLiteral("Not handling device property: "); warningMsg.Append(NS_ConvertUTF16toUTF8(name)); NS_WARNING(warningMsg.get()); } }
--- a/dom/bluetooth/BluetoothDevice.h +++ b/dom/bluetooth/BluetoothDevice.h @@ -53,18 +53,18 @@ public: void Unroot(); private: BluetoothDevice(nsPIDOMWindow* aOwner, const nsAString& aAdapterPath, const BluetoothValue& aValue); ~BluetoothDevice(); void Root(); - JSObject* mJsUuids; - JSObject* mJsServices; + JS::Heap<JSObject*> mJsUuids; + JS::Heap<JSObject*> mJsServices; nsString mAdapterPath; nsString mAddress; nsString mName; nsString mIcon; uint32_t mClass; bool mConnected; bool mPaired;
--- a/dom/future/Future.h +++ b/dom/future/Future.h @@ -102,17 +102,17 @@ private: nsRefPtr<nsPIDOMWindow> mWindow; nsRefPtr<FutureResolver> mResolver; nsTArray<nsRefPtr<FutureCallback> > mResolveCallbacks; nsTArray<nsRefPtr<FutureCallback> > mRejectCallbacks; - JS::Value mResult; + JS::Heap<JS::Value> mResult; FutureState mState; bool mTaskPending; }; } // namespace dom } // namespace mozilla #endif // mozilla_dom_Future_h
--- a/dom/indexedDB/IDBCursor.cpp +++ b/dom/indexedDB/IDBCursor.cpp @@ -539,17 +539,17 @@ IDBCursor::GetKey(JSContext* aCx, } if (!mHaveCachedKey) { if (!mRooted) { NS_HOLD_JS_OBJECTS(this, IDBCursor); mRooted = true; } - nsresult rv = mKey.ToJSVal(aCx, &mCachedKey); + nsresult rv = mKey.ToJSVal(aCx, mCachedKey); NS_ENSURE_SUCCESS(rv, rv); mHaveCachedKey = true; } *aKey = mCachedKey; return NS_OK; } @@ -573,17 +573,17 @@ IDBCursor::GetPrimaryKey(JSContext* aCx, JSAutoRequest ar(aCx); NS_ASSERTION(mType == OBJECTSTORE ? !mKey.IsUnset() : !mObjectKey.IsUnset(), "Bad key!"); const Key& key = mType == OBJECTSTORE ? mKey : mObjectKey; - nsresult rv = key.ToJSVal(aCx, &mCachedPrimaryKey); + nsresult rv = key.ToJSVal(aCx, mCachedPrimaryKey); NS_ENSURE_SUCCESS(rv, rv); mHaveCachedPrimaryKey = true; } *aValue = mCachedPrimaryKey; return NS_OK; } @@ -734,17 +734,17 @@ IDBCursor::Update(const jsval& aValue, rv = mObjectStore->Put(aValue, JSVAL_VOID, aCx, 0, getter_AddRefs(request)); if (NS_FAILED(rv)) { return rv; } } else { JS::Rooted<JS::Value> keyVal(aCx); - rv = objectKey.ToJSVal(aCx, keyVal.address()); + rv = objectKey.ToJSVal(aCx, &keyVal); NS_ENSURE_SUCCESS(rv, rv); rv = mObjectStore->Put(aValue, keyVal, aCx, 1, getter_AddRefs(request)); if (NS_FAILED(rv)) { return rv; } } @@ -805,17 +805,17 @@ IDBCursor::Delete(JSContext* aCx, } NS_ASSERTION(mObjectStore, "This cannot be null!"); NS_ASSERTION(!mKey.IsUnset() , "Bad key!"); Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey; JS::Rooted<JS::Value> key(aCx); - nsresult rv = objectKey.ToJSVal(aCx, key.address()); + nsresult rv = objectKey.ToJSVal(aCx, &key); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIIDBRequest> request; rv = mObjectStore->Delete(key, aCx, getter_AddRefs(request)); if (NS_FAILED(rv)) { return rv; }
--- a/dom/indexedDB/IDBCursor.h +++ b/dom/indexedDB/IDBCursor.h @@ -163,27 +163,27 @@ protected: const nsACString& aContinueQuery, const nsACString& aContinueToQuery); nsRefPtr<IDBRequest> mRequest; nsRefPtr<IDBTransaction> mTransaction; nsRefPtr<IDBObjectStore> mObjectStore; nsRefPtr<IDBIndex> mIndex; - JSObject* mScriptOwner; + JS::Heap<JSObject*> mScriptOwner; Type mType; Direction mDirection; nsCString mContinueQuery; nsCString mContinueToQuery; // These are cycle-collected! - jsval mCachedKey; - jsval mCachedPrimaryKey; - jsval mCachedValue; + JS::Heap<JS::Value> mCachedKey; + JS::Heap<JS::Value> mCachedPrimaryKey; + JS::Heap<JS::Value> mCachedValue; Key mRangeKey; Key mKey; Key mObjectKey; StructuredCloneReadInfo mCloneReadInfo; Key mContinueToKey;
--- a/dom/indexedDB/IDBFactory.h +++ b/dom/indexedDB/IDBFactory.h @@ -178,17 +178,17 @@ private: Open(JSContext* aCx, nsIPrincipal* aPrincipal, const nsAString& aName, const Optional<uint64_t>& aVersion, bool aDelete, ErrorResult& aRv); nsCString mASCIIOrigin; // If this factory lives on a window then mWindow must be non-null. Otherwise // mOwningObject must be non-null. nsCOMPtr<nsPIDOMWindow> mWindow; - JSObject* mOwningObject; + JS::Heap<JSObject*> mOwningObject; IndexedDBChild* mActorChild; IndexedDBParent* mActorParent; mozilla::dom::ContentParent* mContentParent; bool mRootedOwningObject; };
--- a/dom/indexedDB/IDBIndex.cpp +++ b/dom/indexedDB/IDBIndex.cpp @@ -805,17 +805,17 @@ IDBIndex::GetKeyPath(JSContext* aCx, { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); if (!JSVAL_IS_VOID(mCachedKeyPath)) { *aVal = mCachedKeyPath; return NS_OK; } - nsresult rv = GetKeyPath().ToJSVal(aCx, &mCachedKeyPath); + nsresult rv = GetKeyPath().ToJSVal(aCx, mCachedKeyPath); NS_ENSURE_SUCCESS(rv, rv); if (JSVAL_IS_GCTHING(mCachedKeyPath)) { NS_HOLD_JS_OBJECTS(this, IDBIndex); mRooted = true; } *aVal = mCachedKeyPath; @@ -1175,17 +1175,22 @@ GetKeyHelper::DoDatabaseWork(mozIStorage return NS_OK; } nsresult GetKeyHelper::GetSuccessResult(JSContext* aCx, jsval* aVal) { - return mKey.ToJSVal(aCx, aVal); + JS::Rooted<JS::Value> value(aCx); + nsresult rv = mKey.ToJSVal(aCx, &value); + if (NS_SUCCEEDED(rv)) { + *aVal = value; + } + return rv; } void GetKeyHelper::ReleaseMainThreadObjects() { mKeyRange = nullptr; IndexHelper::ReleaseMainThreadObjects(); } @@ -1503,17 +1508,17 @@ GetAllKeysHelper::GetSuccessResult(JSCon return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } for (uint32_t index = 0, count = keys.Length(); index < count; index++) { const Key& key = keys[index]; NS_ASSERTION(!key.IsUnset(), "Bad key!"); JS::Rooted<JS::Value> value(aCx); - nsresult rv = key.ToJSVal(aCx, value.address()); + nsresult rv = key.ToJSVal(aCx, &value); if (NS_FAILED(rv)) { NS_WARNING("Failed to get jsval for key!"); return rv; } if (!JS_SetElement(aCx, array, index, value.address())) { NS_WARNING("Failed to set array element!"); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
--- a/dom/indexedDB/IDBIndex.h +++ b/dom/indexedDB/IDBIndex.h @@ -152,17 +152,17 @@ private: IDBIndex(); ~IDBIndex(); nsRefPtr<IDBObjectStore> mObjectStore; int64_t mId; nsString mName; KeyPath mKeyPath; - JS::Value mCachedKeyPath; + JS::Heap<JS::Value> mCachedKeyPath; IndexedDBIndexChild* mActorChild; IndexedDBIndexParent* mActorParent; bool mUnique; bool mMultiEntry; bool mRooted; };
--- a/dom/indexedDB/IDBKeyRange.cpp +++ b/dom/indexedDB/IDBKeyRange.cpp @@ -358,17 +358,17 @@ IDBKeyRange::GetLower(JSContext* aCx, NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); if (!mHaveCachedLowerVal) { if (!mRooted) { NS_HOLD_JS_OBJECTS(this, IDBKeyRange); mRooted = true; } - nsresult rv = Lower().ToJSVal(aCx, &mCachedLowerVal); + nsresult rv = Lower().ToJSVal(aCx, mCachedLowerVal); NS_ENSURE_SUCCESS(rv, rv); mHaveCachedLowerVal = true; } *aLower = mCachedLowerVal; return NS_OK; } @@ -380,17 +380,17 @@ IDBKeyRange::GetUpper(JSContext* aCx, NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); if (!mHaveCachedUpperVal) { if (!mRooted) { NS_HOLD_JS_OBJECTS(this, IDBKeyRange); mRooted = true; } - nsresult rv = Upper().ToJSVal(aCx, &mCachedUpperVal); + nsresult rv = Upper().ToJSVal(aCx, mCachedUpperVal); NS_ENSURE_SUCCESS(rv, rv); mHaveCachedUpperVal = true; } *aUpper = mCachedUpperVal; return NS_OK; }
--- a/dom/indexedDB/IDBKeyRange.h +++ b/dom/indexedDB/IDBKeyRange.h @@ -152,18 +152,18 @@ public: void DropJSObjects(); private: ~IDBKeyRange(); Key mLower; Key mUpper; - jsval mCachedLowerVal; - jsval mCachedUpperVal; + JS::Heap<JS::Value> mCachedLowerVal; + JS::Heap<JS::Value> mCachedUpperVal; bool mLowerOpen; bool mUpperOpen; bool mIsOnly; bool mHaveCachedLowerVal; bool mHaveCachedUpperVal; bool mRooted; };
--- a/dom/indexedDB/IDBObjectStore.cpp +++ b/dom/indexedDB/IDBObjectStore.cpp @@ -2382,17 +2382,17 @@ IDBObjectStore::GetKeyPath(JSContext* aC { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); if (!JSVAL_IS_VOID(mCachedKeyPath)) { *aVal = mCachedKeyPath; return NS_OK; } - nsresult rv = GetKeyPath().ToJSVal(aCx, &mCachedKeyPath); + nsresult rv = GetKeyPath().ToJSVal(aCx, mCachedKeyPath); NS_ENSURE_SUCCESS(rv, rv); if (JSVAL_IS_GCTHING(mCachedKeyPath)) { NS_HOLD_JS_OBJECTS(this, IDBObjectStore); mRooted = true; } *aVal = mCachedKeyPath; @@ -3129,17 +3129,22 @@ AddHelper::DoDatabaseWork(mozIStorageCon nsresult AddHelper::GetSuccessResult(JSContext* aCx, jsval* aVal) { NS_ASSERTION(!mKey.IsUnset(), "Badness!"); mCloneWriteInfo.mCloneBuffer.clear(); - return mKey.ToJSVal(aCx, aVal); + JS::Rooted<JS::Value> value(aCx); + nsresult rv = mKey.ToJSVal(aCx, &value); + if (NS_SUCCEEDED(rv)) { + *aVal = value; + } + return rv; } void AddHelper::ReleaseMainThreadObjects() { IDBObjectStore::ClearCloneWriteInfo(mCloneWriteInfo); ObjectStoreHelper::ReleaseMainThreadObjects(); }
--- a/dom/indexedDB/IDBObjectStore.h +++ b/dom/indexedDB/IDBObjectStore.h @@ -282,17 +282,17 @@ protected: uint32_t aTag, BlobOrFileData* aRetval); private: nsRefPtr<IDBTransaction> mTransaction; int64_t mId; nsString mName; KeyPath mKeyPath; - JS::Value mCachedKeyPath; + JS::Heap<JS::Value> mCachedKeyPath; bool mRooted; bool mAutoIncrement; nsCOMPtr<nsIAtom> mDatabaseId; nsRefPtr<ObjectStoreInfo> mInfo; nsTArray<nsRefPtr<IDBIndex> > mCreatedIndexes; IndexedDBObjectStoreChild* mActorChild;
--- a/dom/indexedDB/IDBRequest.cpp +++ b/dom/indexedDB/IDBRequest.cpp @@ -115,23 +115,25 @@ IDBRequest::NotifyHelperCompleted(Helper } JS::Rooted<JSObject*> global(cx, GetParentObject()); NS_ASSERTION(global, "This should never be null!"); JSAutoCompartment ac(cx, global); AssertIsRooted(); - rv = aHelper->GetSuccessResult(cx, &mResultVal); + JS::Rooted<JS::Value> value(cx); + rv = aHelper->GetSuccessResult(cx, value.address()); if (NS_FAILED(rv)) { NS_WARNING("GetSuccessResult failed!"); } if (NS_SUCCEEDED(rv)) { mError = nullptr; + mResultVal = value; } else { SetError(rv); mResultVal = JSVAL_VOID; } return rv; }
--- a/dom/indexedDB/IDBRequest.h +++ b/dom/indexedDB/IDBRequest.h @@ -104,17 +104,17 @@ public: protected: IDBRequest(); ~IDBRequest(); nsCOMPtr<nsISupports> mSource; nsRefPtr<IDBTransaction> mTransaction; - jsval mResultVal; + JS::Heap<JS::Value> mResultVal; nsRefPtr<mozilla::dom::DOMError> mError; IndexedDBRequestParentBase* mActorParent; nsString mFilename; #ifdef MOZ_ENABLE_PROFILER_SPS uint64_t mSerialNumber; #endif nsresult mErrorCode; uint32_t mLineNo;
--- a/dom/indexedDB/IDBWrapperCache.h +++ b/dom/indexedDB/IDBWrapperCache.h @@ -57,14 +57,14 @@ public: protected: IDBWrapperCache() : mScriptOwner(nullptr) { } virtual ~IDBWrapperCache(); private: - JSObject* mScriptOwner; + JS::Heap<JSObject*> mScriptOwner; }; END_INDEXEDDB_NAMESPACE #endif // mozilla_dom_indexeddb_idbwrappercache_h__
--- a/dom/indexedDB/Key.cpp +++ b/dom/indexedDB/Key.cpp @@ -180,17 +180,17 @@ Key::EncodeJSValInternal(JSContext* aCx, } return NS_ERROR_DOM_INDEXEDDB_DATA_ERR; } // static nsresult Key::DecodeJSValInternal(const unsigned char*& aPos, const unsigned char* aEnd, - JSContext* aCx, uint8_t aTypeOffset, jsval* aVal, + JSContext* aCx, uint8_t aTypeOffset, JS::MutableHandle<JS::Value> aVal, uint16_t aRecursionDepth) { NS_ENSURE_TRUE(aRecursionDepth < MaxRecursionDepth, NS_ERROR_DOM_INDEXEDDB_DATA_ERR); if (*aPos - aTypeOffset >= eArray) { JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, 0, nullptr)); if (!array) { NS_WARNING("Failed to make array!"); @@ -203,52 +203,52 @@ Key::DecodeJSValInternal(const unsigned ++aPos; aTypeOffset = 0; } uint32_t index = 0; while (aPos < aEnd && *aPos - aTypeOffset != eTerminator) { JS::Rooted<JS::Value> val(aCx); nsresult rv = DecodeJSValInternal(aPos, aEnd, aCx, aTypeOffset, - val.address(), aRecursionDepth + 1); + &val, aRecursionDepth + 1); NS_ENSURE_SUCCESS(rv, rv); aTypeOffset = 0; if (!JS_SetElement(aCx, array, index++, val.address())) { NS_WARNING("Failed to set array element!"); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } } NS_ASSERTION(aPos >= aEnd || (*aPos % eMaxType) == eTerminator, "Should have found end-of-array marker"); ++aPos; - *aVal = OBJECT_TO_JSVAL(array); + aVal.setObject(*array); } else if (*aPos - aTypeOffset == eString) { nsString key; DecodeString(aPos, aEnd, key); - if (!xpc::StringToJsval(aCx, key, aVal)) { + if (!xpc::StringToJsval(aCx, key, aVal.address())) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } } else if (*aPos - aTypeOffset == eDate) { double msec = static_cast<double>(DecodeNumber(aPos, aEnd)); JSObject* date = JS_NewDateObjectMsec(aCx, msec); if (!date) { NS_WARNING("Failed to make date!"); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } - *aVal = OBJECT_TO_JSVAL(date); + aVal.setObject(*date); } else if (*aPos - aTypeOffset == eFloat) { - *aVal = DOUBLE_TO_JSVAL(DecodeNumber(aPos, aEnd)); + aVal.setDouble(DecodeNumber(aPos, aEnd)); } else { NS_NOTREACHED("Unknown key type!"); } return NS_OK; }
--- a/dom/indexedDB/Key.h +++ b/dom/indexedDB/Key.h @@ -174,33 +174,44 @@ public: return rv; } TrimBuffer(); return NS_OK; } nsresult ToJSVal(JSContext* aCx, - jsval* aVal) const + JS::MutableHandle<JS::Value> aVal) const { if (IsUnset()) { - *aVal = JSVAL_VOID; + aVal.set(JSVAL_VOID); return NS_OK; } const unsigned char* pos = BufferStart(); nsresult rv = DecodeJSVal(pos, BufferEnd(), aCx, 0, aVal); NS_ENSURE_SUCCESS(rv, rv); NS_ASSERTION(pos >= BufferEnd(), "Didn't consume whole buffer"); return NS_OK; } + nsresult ToJSVal(JSContext* aCx, + JS::Heap<JS::Value>& aVal) const + { + JS::Rooted<JS::Value> value(aCx); + nsresult rv = ToJSVal(aCx, &value); + if (NS_SUCCEEDED(rv)) { + aVal = value; + } + return rv; + } + nsresult AppendItem(JSContext* aCx, bool aFirstOfArray, const jsval aVal) { nsresult rv = EncodeJSVal(aCx, aVal, aFirstOfArray ? eMaxType : 0); if (NS_FAILED(rv)) { Unset(); return rv; @@ -299,17 +310,17 @@ private: } void EncodeString(const nsAString& aString, uint8_t aTypeOffset); void EncodeNumber(double aFloat, uint8_t aType); // Decoding functions. aPos points into mBuffer and is adjusted to point // past the consumed value. static inline nsresult DecodeJSVal(const unsigned char*& aPos, const unsigned char* aEnd, JSContext* aCx, - uint8_t aTypeOffset, jsval* aVal) + uint8_t aTypeOffset, JS::MutableHandle<JS::Value> aVal) { return DecodeJSValInternal(aPos, aEnd, aCx, aTypeOffset, aVal, 0); } static void DecodeString(const unsigned char*& aPos, const unsigned char* aEnd, nsString& aString); static double DecodeNumber(const unsigned char*& aPos, @@ -319,14 +330,14 @@ private: private: nsresult EncodeJSValInternal(JSContext* aCx, const jsval aVal, uint8_t aTypeOffset, uint16_t aRecursionDepth); static nsresult DecodeJSValInternal(const unsigned char*& aPos, const unsigned char* aEnd, JSContext* aCx, uint8_t aTypeOffset, - jsval* aVal, uint16_t aRecursionDepth); + JS::MutableHandle<JS::Value> aVal, uint16_t aRecursionDepth); }; END_INDEXEDDB_NAMESPACE #endif /* mozilla_dom_indexeddb_key_h__ */
--- a/dom/indexedDB/KeyPath.cpp +++ b/dom/indexedDB/KeyPath.cpp @@ -452,17 +452,17 @@ KeyPath::DeserializeFromString(const nsA keyPath.SetType(STRING); keyPath.mStrings.AppendElement(aString); return keyPath; } nsresult -KeyPath::ToJSVal(JSContext* aCx, JS::Value* aValue) const +KeyPath::ToJSVal(JSContext* aCx, JS::MutableHandle<JS::Value> aValue) const { if (IsArray()) { uint32_t len = mStrings.Length(); JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, len, nullptr)); if (!array) { NS_WARNING("Failed to make array!"); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } @@ -474,32 +474,43 @@ KeyPath::ToJSVal(JSContext* aCx, JS::Val return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } if (!JS_SetElement(aCx, array, i, val.address())) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } } - *aValue = OBJECT_TO_JSVAL(array); + aValue.setObject(*array); return NS_OK; } if (IsString()) { nsString tmp(mStrings[0]); - if (!xpc::StringToJsval(aCx, tmp, aValue)) { + if (!xpc::StringToJsval(aCx, tmp, aValue.address())) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } return NS_OK; } - *aValue = JSVAL_NULL; + aValue.setNull(); return NS_OK; } +nsresult +KeyPath::ToJSVal(JSContext* aCx, JS::Heap<JS::Value>& aValue) const +{ + JS::Rooted<JS::Value> value(aCx); + nsresult rv = ToJSVal(aCx, &value); + if (NS_SUCCEEDED(rv)) { + aValue = value; + } + return rv; +} + bool KeyPath::IsAllowedForObjectStore(bool aAutoIncrement) const { // Any keypath that passed validation is allowed for non-autoIncrement // objectStores. if (!aAutoIncrement) { return true; }
--- a/dom/indexedDB/KeyPath.h +++ b/dom/indexedDB/KeyPath.h @@ -82,17 +82,18 @@ public: bool operator==(const KeyPath& aOther) const { return mType == aOther.mType && mStrings == aOther.mStrings; } void SerializeToString(nsAString& aString) const; static KeyPath DeserializeFromString(const nsAString& aString); - nsresult ToJSVal(JSContext* aCx, JS::Value* aValue) const; + nsresult ToJSVal(JSContext* aCx, JS::MutableHandle<JS::Value> aValue) const; + nsresult ToJSVal(JSContext* aCx, JS::Heap<JS::Value>& aValue) const; bool IsAllowedForObjectStore(bool aAutoIncrement) const; KeyPathType mType; nsTArray<nsString> mStrings; };
--- a/dom/plugins/base/nsPluginHost.cpp +++ b/dom/plugins/base/nsPluginHost.cpp @@ -1159,22 +1159,17 @@ public: NS_METHOD GetDescription(nsAString& aDescription) { CopyUTF8toUTF16(mPluginTag.mDescription, aDescription); return NS_OK; } NS_METHOD GetFilename(nsAString& aFilename) { - if (Preferences::GetBool("plugin.expose_full_path", false)) { - CopyUTF8toUTF16(mPluginTag.mFullPath, aFilename); - } else { - CopyUTF8toUTF16(mPluginTag.mFileName, aFilename); - } - + CopyUTF8toUTF16(mPluginTag.mFileName, aFilename); return NS_OK; } NS_METHOD GetVersion(nsAString& aVersion) { CopyUTF8toUTF16(mPluginTag.mVersion, aVersion); return NS_OK; }
--- a/dom/telephony/Telephony.h +++ b/dom/telephony/Telephony.h @@ -36,17 +36,17 @@ class Telephony : public nsDOMEventTarge nsCOMPtr<nsITelephonyProvider> mProvider; nsRefPtr<Listener> mListener; TelephonyCall* mActiveCall; nsTArray<nsRefPtr<TelephonyCall> > mCalls; // Cached calls array object. Cleared whenever mCalls changes and then rebuilt // once a page looks for the liveCalls attribute. - JSObject* mCallsArray; + JS::Heap<JSObject*> mCallsArray; bool mRooted; bool mEnumerated; public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIDOMTELEPHONY NS_DECL_NSITELEPHONYLISTENER
--- a/gfx/2d/SourceSurfaceSkia.cpp +++ b/gfx/2d/SourceSurfaceSkia.cpp @@ -10,22 +10,23 @@ #include "skia/SkDevice.h" #include "HelpersSkia.h" #include "DrawTargetSkia.h" namespace mozilla { namespace gfx { SourceSurfaceSkia::SourceSurfaceSkia() - : mDrawTarget(nullptr) + : mDrawTarget(nullptr), mLocked(false) { } SourceSurfaceSkia::~SourceSurfaceSkia() { + MaybeUnlock(); MarkIndependent(); } IntSize SourceSurfaceSkia::GetSize() const { return mSize; } @@ -80,27 +81,31 @@ SourceSurfaceSkia::InitFromData(unsigned mFormat = aFormat; mStride = aStride; return true; } unsigned char* SourceSurfaceSkia::GetData() { - mBitmap.lockPixels(); + if (!mLocked) { + mBitmap.lockPixels(); + mLocked = true; + } + unsigned char *pixels = (unsigned char *)mBitmap.getPixels(); - mBitmap.unlockPixels(); return pixels; - } void SourceSurfaceSkia::DrawTargetWillChange() { if (mDrawTarget) { + MaybeUnlock(); + mDrawTarget = nullptr; SkBitmap temp = mBitmap; mBitmap.reset(); temp.copyTo(&mBitmap, temp.getConfig()); } } void @@ -113,10 +118,19 @@ void SourceSurfaceSkia::MarkIndependent() { if (mDrawTarget) { mDrawTarget->RemoveSnapshot(this); mDrawTarget = nullptr; } } +void +SourceSurfaceSkia::MaybeUnlock() +{ + if (mLocked) { + mBitmap.unlockPixels(); + mLocked = false; + } +} + } }
--- a/gfx/2d/SourceSurfaceSkia.h +++ b/gfx/2d/SourceSurfaceSkia.h @@ -42,20 +42,22 @@ public: virtual int32_t Stride() { return mStride; } private: friend class DrawTargetSkia; void DrawTargetWillChange(); void DrawTargetDestroyed(); void MarkIndependent(); + void MaybeUnlock(); SkBitmap mBitmap; SurfaceFormat mFormat; IntSize mSize; int32_t mStride; DrawTargetSkia* mDrawTarget; + bool mLocked; }; } } #endif /* MOZILLA_GFX_SOURCESURFACESKIA_H_ */
--- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -461,16 +461,31 @@ public: */ virtual void ClearCachedResources(Layer* aSubtree = nullptr) {} /** * Flag the next paint as the first for a document. */ virtual void SetIsFirstPaint() {} + /** + * Make sure that the previous transaction has been entirely + * completed. + * + * Note: This may sychronously wait on a remote compositor + * to complete rendering. + */ + virtual void FlushRendering() { } + + /** + * Checks if we need to invalidate the OS widget to trigger + * painting when updating this layer manager. + */ + virtual bool NeedsWidgetInvalidation() { return true; } + // We always declare the following logging symbols, because it's // extremely tricky to conditionally declare them. However, for // ifndef MOZ_LAYERS_HAVE_LOG builds, they only have trivial // definitions in Layers.cpp. virtual const char* Name() const { return "???"; } /** * Dump information about this layer manager and its managed tree to
--- a/gfx/layers/client/ClientLayerManager.cpp +++ b/gfx/layers/client/ClientLayerManager.cpp @@ -256,16 +256,26 @@ ClientLayerManager::MakeSnapshotIfRequir ShadowLayerForwarder::DestroySharedSurface(&snapshot); } } } mShadowTarget = nullptr; } void +ClientLayerManager::FlushRendering() +{ + if (mWidget) { + if (CompositorChild* remoteRenderer = mWidget->GetRemoteRenderer()) { + remoteRenderer->SendFlushRendering(); + } + } +} + +void ClientLayerManager::ForwardTransaction() { mPhase = PHASE_FORWARD; // forward this transaction's changeset to our LayerManagerComposite AutoInfallibleTArray<EditReply, 10> replies; if (HasShadowManager() && ShadowLayerForwarder::EndTransaction(&replies)) { for (nsTArray<EditReply>::size_type i = 0; i < replies.Length(); ++i) {
--- a/gfx/layers/client/ClientLayerManager.h +++ b/gfx/layers/client/ClientLayerManager.h @@ -48,16 +48,20 @@ public: virtual already_AddRefed<ThebesLayer> CreateThebesLayer(); virtual already_AddRefed<ContainerLayer> CreateContainerLayer(); virtual already_AddRefed<ImageLayer> CreateImageLayer(); virtual already_AddRefed<CanvasLayer> CreateCanvasLayer(); virtual already_AddRefed<ColorLayer> CreateColorLayer(); virtual already_AddRefed<RefLayer> CreateRefLayer(); + virtual void FlushRendering() MOZ_OVERRIDE; + + virtual bool NeedsWidgetInvalidation() MOZ_OVERRIDE { return Compositor::GetBackend() == LAYERS_BASIC; } + ShadowableLayer* Hold(Layer* aLayer); bool HasShadowManager() const { return ShadowLayerForwarder::HasShadowManager(); } virtual bool IsCompositingCheap(); virtual bool HasShadowManagerInternal() const { return HasShadowManager(); } virtual void SetIsFirstPaint() MOZ_OVERRIDE;
--- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -244,16 +244,28 @@ CompositorParent::RecvMakeSnapshot(const { AutoOpenSurface opener(OPEN_READ_WRITE, aInSnapshot); nsRefPtr<gfxContext> target = new gfxContext(opener.Get()); ComposeToTarget(target); *aOutSnapshot = aInSnapshot; return true; } +bool +CompositorParent::RecvFlushRendering() +{ + // If we're waiting to do a composite, then cancel it + // and do it immediately instead. + if (mCurrentCompositeTask) { + mCurrentCompositeTask->Cancel(); + ComposeToTarget(nullptr); + } + return true; +} + void CompositorParent::ActorDestroy(ActorDestroyReason why) { mPaused = true; RemoveCompositor(mCompositorID); if (mLayerManager) { mLayerManager->Destroy(); @@ -738,16 +750,17 @@ public: // FIXME/bug 774388: work out what shutdown protocol we need. virtual bool RecvWillStop() MOZ_OVERRIDE { return true; } virtual bool RecvStop() MOZ_OVERRIDE { return true; } virtual bool RecvPause() MOZ_OVERRIDE { return true; } virtual bool RecvResume() MOZ_OVERRIDE { return true; } virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot, SurfaceDescriptor* aOutSnapshot) { return true; } + virtual bool RecvFlushRendering() MOZ_OVERRIDE { return true; } virtual PLayerTransactionParent* AllocPLayerTransaction(const LayersBackend& aBackendType, const uint64_t& aId, TextureFactoryIdentifier* aTextureFactoryIdentifier) MOZ_OVERRIDE; virtual bool DeallocPLayerTransaction(PLayerTransactionParent* aLayers) MOZ_OVERRIDE;
--- a/gfx/layers/ipc/CompositorParent.h +++ b/gfx/layers/ipc/CompositorParent.h @@ -50,16 +50,17 @@ public: virtual ~CompositorParent(); virtual bool RecvWillStop() MOZ_OVERRIDE; virtual bool RecvStop() MOZ_OVERRIDE; virtual bool RecvPause() MOZ_OVERRIDE; virtual bool RecvResume() MOZ_OVERRIDE; virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot, SurfaceDescriptor* aOutSnapshot); + virtual bool RecvFlushRendering() MOZ_OVERRIDE; virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE; virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree, const TargetConfig& aTargetConfig, bool isFirstPaint) MOZ_OVERRIDE; /** * This forces the is-first-paint flag to true. This is intended to
--- a/gfx/layers/ipc/PCompositor.ipdl +++ b/gfx/layers/ipc/PCompositor.ipdl @@ -48,14 +48,18 @@ parent: // or format of |inSnapshot| doesn't match our render target, // results are undefined. // // NB: this message will result in animations, transforms, effects, // and so forth being interpolated. That's what we want to happen. sync MakeSnapshot(SurfaceDescriptor inSnapshot) returns (SurfaceDescriptor outSnapshot); + // Make sure any pending composites are started immediately and + // block until they are completed. + sync FlushRendering(); + sync PLayerTransaction(LayersBackend layersBackendHint, uint64_t id) returns (TextureFactoryIdentifier textureFactoryIdentifier); }; } // layers } // mozilla
--- a/js/public/RootingAPI.h +++ b/js/public/RootingAPI.h @@ -176,28 +176,28 @@ struct JS_PUBLIC_API(NullPtr) */ template <typename T> class Heap : public js::HeapBase<T> { public: Heap() { MOZ_STATIC_ASSERT(sizeof(T) == sizeof(Heap<T>), "Heap<T> must be binary compatible with T."); - set(js::RootMethods<T>::initial()); + init(js::RootMethods<T>::initial()); } - explicit Heap(T p) { set(p); } - explicit Heap(const Heap<T> &p) { set(p.ptr); } + explicit Heap(T p) { init(p); } + explicit Heap(const Heap<T> &p) { init(p.ptr); } ~Heap() { if (js::RootMethods<T>::needsPostBarrier(ptr)) relocate(); } - bool operator!=(const T &other) { return *ptr != other; } - bool operator==(const T &other) { return *ptr == other; } + bool operator!=(const T &other) const { return ptr != other; } + bool operator==(const T &other) const { return ptr == other; } operator T() const { return ptr; } T operator->() const { return ptr; } const T *address() const { return &ptr; } const T &get() const { return ptr; } T *unsafeGet() { return &ptr; } @@ -215,16 +215,23 @@ class Heap : public js::HeapBase<T> relocate(); /* Called before overwriting ptr. */ ptr = newPtr; } else { ptr = newPtr; } } private: + void init(T newPtr) { + JS_ASSERT(!js::RootMethods<T>::poisoned(newPtr)); + ptr = newPtr; + if (js::RootMethods<T>::needsPostBarrier(ptr)) + post(); + } + void post() { #ifdef JSGC_GENERATIONAL JS_ASSERT(js::RootMethods<T>::needsPostBarrier(ptr)); js::RootMethods<T>::postBarrier(&ptr); #endif } void relocate() { @@ -315,18 +322,18 @@ class MOZ_NONHEAP_CLASS Handle : public /* * Return a reference so passing a Handle<T> to something that * takes a |const T&| is not a GC hazard. */ operator const T&() const { return get(); } T operator->() const { return get(); } - bool operator!=(const T &other) { return *ptr != other; } - bool operator==(const T &other) { return *ptr == other; } + bool operator!=(const T &other) const { return *ptr != other; } + bool operator==(const T &other) const { return *ptr == other; } private: Handle() {} const T *ptr; template <typename S> void operator=(S v) MOZ_DELETE; @@ -600,18 +607,18 @@ class MOZ_STACK_CLASS Rooted : public js return ptr; } void set(T value) { JS_ASSERT(!js::RootMethods<T>::poisoned(value)); ptr = value; } - bool operator!=(const T &other) { return ptr != other; } - bool operator==(const T &other) { return ptr == other; } + bool operator!=(const T &other) const { return ptr != other; } + bool operator==(const T &other) const { return ptr == other; } private: void commonInit(Rooted<void*> **thingGCRooters) { #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) js::ThingRootKind kind = js::RootMethods<T>::kind(); this->stack = &thingGCRooters[kind]; this->prev = *stack; *stack = reinterpret_cast<Rooted<void*>*>(this); @@ -761,18 +768,18 @@ class FakeRooted : public RootedBase<T> const T &get() const { return ptr; } T &operator=(T value) { JS_ASSERT(!RootMethods<T>::poisoned(value)); ptr = value; return ptr; } - bool operator!=(const T &other) { return ptr != other; } - bool operator==(const T &other) { return ptr == other; } + bool operator!=(const T &other) const { return ptr != other; } + bool operator==(const T &other) const { return ptr == other; } private: T ptr; MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER FakeRooted(const FakeRooted &) MOZ_DELETE; };
--- a/js/public/Value.h +++ b/js/public/Value.h @@ -1415,28 +1415,31 @@ template <> struct RootMethods<JS::Value static bool poisoned(const JS::Value &v) { return JS::IsPoisonedValue(v); } static bool needsPostBarrier(const JS::Value &v) { return v.isMarkable(); } #ifdef JSGC_GENERATIONAL static void postBarrier(JS::Value *v) { JS::HeapValuePostBarrier(v); } static void relocate(JS::Value *v) { JS::HeapValueRelocate(v); } #endif }; +template <class Outer> class UnbarrieredMutableValueOperations; template <class Outer> class MutableValueOperations; /* * A class designed for CRTP use in implementing the non-mutating parts of the * Value interface in Value-like classes. Outer must be a class inheriting * ValueOperations<Outer> with a visible extract() method returning the * const Value* abstracted by Outer. */ template <class Outer> class ValueOperations { + friend class UnbarrieredMutableValueOperations<Outer>; friend class MutableValueOperations<Outer>; + const JS::Value * value() const { return static_cast<const Outer*>(this)->extract(); } public: bool isUndefined() const { return value()->isUndefined(); } bool isNull() const { return value()->isNull(); } bool isBoolean() const { return value()->isBoolean(); } bool isTrue() const { return value()->isTrue(); } bool isFalse() const { return value()->isFalse(); } @@ -1464,51 +1467,92 @@ class ValueOperations void *toGCThing() const { return value()->toGCThing(); } JSValueType extractNonDoubleType() const { return value()->extractNonDoubleType(); } JSWhyMagic whyMagic() const { return value()->whyMagic(); } }; /* - * A class designed for CRTP use in implementing the mutating parts of the - * Value interface in Value-like classes. Outer must be a class inheriting - * MutableValueOperations<Outer> with visible extractMutable() and extract() - * methods returning the const Value* and Value* abstracted by Outer. + * A class designed for CRTP use in implementing the mutating parts of the Value + * interface in Value-like classes that don't need post barriers. Outer must be + * a class inheriting UnbarrieredMutableValueOperations<Outer> with visible + * extractMutable() and extract() methods returning the const Value* and Value* + * abstracted by Outer. */ template <class Outer> -class MutableValueOperations : public ValueOperations<Outer> +class UnbarrieredMutableValueOperations : public ValueOperations<Outer> { + friend class MutableValueOperations<Outer>; JS::Value * value() { return static_cast<Outer*>(this)->extractMutable(); } public: void setNull() { value()->setNull(); } void setUndefined() { value()->setUndefined(); } void setInt32(int32_t i) { value()->setInt32(i); } void setDouble(double d) { value()->setDouble(d); } - void setString(JSString *str) { value()->setString(str); } - void setString(const JS::Anchor<JSString *> &str) { value()->setString(str); } - void setObject(JSObject &obj) { value()->setObject(obj); } void setBoolean(bool b) { value()->setBoolean(b); } void setMagic(JSWhyMagic why) { value()->setMagic(why); } bool setNumber(uint32_t ui) { return value()->setNumber(ui); } bool setNumber(double d) { return value()->setNumber(d); } - void setObjectOrNull(JSObject *arg) { value()->setObjectOrNull(arg); } +}; + +/* + * A class designed for CRTP use in implementing all the mutating parts of the + * Value interface in Value-like classes. Outer must be a class inheriting + * MutableValueOperations<Outer> with visible extractMutable() and extract() + * methods returning the const Value* and Value* abstracted by Outer. + */ +template <class Outer> +class MutableValueOperations : public UnbarrieredMutableValueOperations<Outer> +{ + public: + void setString(JSString *str) { this->value()->setString(str); } + void setString(const JS::Anchor<JSString *> &str) { this->value()->setString(str); } + void setObject(JSObject &obj) { this->value()->setObject(obj); } + void setObjectOrNull(JSObject *arg) { this->value()->setObjectOrNull(arg); } }; /* - * Augment the generic Heap<T> interface when T = Value with type-querying - * and value-extracting operations. + * Augment the generic Heap<T> interface when T = Value with + * type-querying, value-extracting, and mutating operations. */ template <> -class HeapBase<JS::Value> : public ValueOperations<JS::Heap<JS::Value> > +class HeapBase<JS::Value> : public UnbarrieredMutableValueOperations<JS::Heap<JS::Value> > { - friend class ValueOperations<JS::Heap<JS::Value> >; - const JS::Value * extract() const { - return static_cast<const JS::Heap<JS::Value>*>(this)->address(); + typedef JS::Heap<JS::Value> Outer; + + friend class ValueOperations<Outer>; + friend class UnbarrieredMutableValueOperations<Outer>; + + const JS::Value * extract() const { return static_cast<const Outer*>(this)->address(); } + JS::Value * extractMutable() { return static_cast<Outer*>(this)->unsafeGet(); } + + /* + * Setters that potentially change the value to a GC thing from a non-GC + * thing must call JS::Heap::set() to trigger the post barrier. + * + * Changing from a GC thing to a non-GC thing value will leave the heap + * value in the store buffer, but it will be ingored so this is not a + * problem. + */ + void setBarriered(const JS::Value &v) { + static_cast<JS::Heap<JS::Value> *>(this)->set(v); + } + + public: + void setString(JSString *str) { setBarriered(JS::StringValue(str)); } + void setString(const JS::Anchor<JSString *> &str) { setBarriered(JS::StringValue(str.get())); } + void setObject(JSObject &obj) { setBarriered(JS::ObjectValue(obj)); } + + void setObjectOrNull(JSObject *arg) { + if (arg) + setObject(*arg); + else + setNull(); } }; /* * Augment the generic Handle<T> interface when T = Value with type-querying * and value-extracting operations. */ template <> @@ -1527,16 +1571,17 @@ class HandleBase<JS::Value> : public Val template <> class MutableHandleBase<JS::Value> : public MutableValueOperations<JS::MutableHandle<JS::Value> > { friend class ValueOperations<JS::MutableHandle<JS::Value> >; const JS::Value * extract() const { return static_cast<const JS::MutableHandle<JS::Value>*>(this)->address(); } + friend class UnbarrieredMutableValueOperations<JS::MutableHandle<JS::Value> >; friend class MutableValueOperations<JS::MutableHandle<JS::Value> >; JS::Value * extractMutable() { return static_cast<JS::MutableHandle<JS::Value>*>(this)->address(); } }; /* * Augment the generic Rooted<T> interface when T = Value with type-querying, @@ -1545,16 +1590,17 @@ class MutableHandleBase<JS::Value> : pub template <> class RootedBase<JS::Value> : public MutableValueOperations<JS::Rooted<JS::Value> > { friend class ValueOperations<JS::Rooted<JS::Value> >; const JS::Value * extract() const { return static_cast<const JS::Rooted<JS::Value>*>(this)->address(); } + friend class UnbarrieredMutableValueOperations<JS::Rooted<JS::Value> >; friend class MutableValueOperations<JS::Rooted<JS::Value> >; JS::Value * extractMutable() { return static_cast<JS::Rooted<JS::Value>*>(this)->address(); } }; } // namespace js
--- a/js/src/build/autoconf/android.m4 +++ b/js/src/build/autoconf/android.m4 @@ -300,25 +300,40 @@ case "$target" in AC_MSG_ERROR([The given Android SDK provides API level $android_api_level ($1 or higher required).]) fi fi android_platform_tools="$android_sdk"/../../platform-tools if test ! -d "$android_platform_tools" ; then android_platform_tools="$android_sdk"/tools # SDK Tools < r8 fi + # The build tools got moved around to different directories in + # SDK Tools r22. Try to locate them. + android_build_tools="" + for suffix in 17.0.0 android-4.2.2; do + tools_directory="$android_sdk/../../build-tools/$suffix" + if test -d "$tools_directory" ; then + android_build_tools="$tools_directory" + break + fi + done + if test -z "$android_build_tools" ; then + android_build_tools="$android_platform_tools" # SDK Tools < r22 + fi ANDROID_SDK="${android_sdk}" if test -e "${android_sdk}/../../extras/android/compatibility/v4/android-support-v4.jar" ; then ANDROID_COMPAT_LIB="${android_sdk}/../../extras/android/compatibility/v4/android-support-v4.jar" else ANDROID_COMPAT_LIB="${android_sdk}/../../extras/android/support/v4/android-support-v4.jar"; fi ANDROID_PLATFORM_TOOLS="${android_platform_tools}" + ANDROID_BUILD_TOOLS="${android_build_tools}" AC_SUBST(ANDROID_SDK) AC_SUBST(ANDROID_COMPAT_LIB) if ! test -e $ANDROID_COMPAT_LIB ; then AC_MSG_ERROR([You must download the Android support library when targeting Android. Run the Android SDK tool and install Android Support Library under Extras. See https://developer.android.com/tools/extras/support-library.html for more info. (looked for $ANDROID_COMPAT_LIB)]) fi AC_SUBST(ANDROID_PLATFORM_TOOLS) + AC_SUBST(ANDROID_BUILD_TOOLS) ;; esac ])
--- a/js/src/config/Makefile.in +++ b/js/src/config/Makefile.in @@ -11,17 +11,16 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk # For sanity's sake, we compile nsinstall without the wrapped system # headers, so that we can use it to set up the wrapped system headers. VISIBILITY_FLAGS = ifneq (WINNT,$(HOST_OS_ARCH)) HOST_PROGRAM = nsinstall_real$(HOST_BIN_SUFFIX) -DISABLED_HOST_CSRCS = nsinstall.c pathsub.c endif # IMPORTANT: Disable NSBUILDROOT for this directory only, otherwise we have # a recursive rule for finding nsinstall and the Perl scripts. ifdef NSBUILDROOT override NSBUILDROOT := endif
--- a/js/src/config/rules.mk +++ b/js/src/config/rules.mk @@ -11,16 +11,17 @@ ifndef topsrcdir endif # Integrate with mozbuild-generated make files. We first verify that no # variables provided by the automatically generated .mk files are # present. If they are, this is a violation of the separation of # responsibility between Makefile.in and mozbuild files. _MOZBUILD_EXTERNAL_VARIABLES := \ DIRS \ + HOST_CSRCS \ MODULE \ PARALLEL_DIRS \ TEST_DIRS \ TIERS \ TOOL_DIRS \ XPIDL_MODULE \ $(NULL)
--- a/js/src/ion/BaselineIC.cpp +++ b/js/src/ion/BaselineIC.cpp @@ -2544,19 +2544,20 @@ DoBinaryArithFallback(JSContext *cx, Bas stub->addNewStub(doubleStub); return true; } default: break; } } - // TODO: unlink previous !allowDouble stub. if (lhs.isInt32() && rhs.isInt32()) { bool allowDouble = ret.isDouble(); + if (allowDouble) + stub->unlinkStubsWithKind(cx, ICStub::BinaryArith_Int32); IonSpew(IonSpew_BaselineIC, " Generating %s(Int32, Int32%s) stub", js_CodeName[op], allowDouble ? " => Double" : ""); ICBinaryArith_Int32::Compiler compilerInt32(cx, op, allowDouble); ICStub *int32Stub = compilerInt32.getStub(compilerInt32.getStubSpace(script)); if (!int32Stub) return false; stub->addNewStub(int32Stub); return true;
--- a/js/src/ion/BaselineIC.h +++ b/js/src/ion/BaselineIC.h @@ -2400,24 +2400,30 @@ class ICBinaryArith_Fallback : public IC } }; }; class ICBinaryArith_Int32 : public ICStub { friend class ICStubSpace; - ICBinaryArith_Int32(IonCode *stubCode) - : ICStub(BinaryArith_Int32, stubCode) {} + ICBinaryArith_Int32(IonCode *stubCode, bool allowDouble) + : ICStub(BinaryArith_Int32, stubCode) + { + extra_ = allowDouble; + } public: - static inline ICBinaryArith_Int32 *New(ICStubSpace *space, IonCode *code) { + static inline ICBinaryArith_Int32 *New(ICStubSpace *space, IonCode *code, bool allowDouble) { if (!code) return NULL; - return space->allocate<ICBinaryArith_Int32>(code); + return space->allocate<ICBinaryArith_Int32>(code, allowDouble); + } + bool allowDouble() const { + return extra_; } // Compiler for this stub kind. class Compiler : public ICStubCompiler { protected: JSOp op_; bool allowDouble_; @@ -2430,17 +2436,17 @@ class ICBinaryArith_Int32 : public ICStu } public: Compiler(JSContext *cx, JSOp op, bool allowDouble) : ICStubCompiler(cx, ICStub::BinaryArith_Int32), op_(op), allowDouble_(allowDouble) {} ICStub *getStub(ICStubSpace *space) { - return ICBinaryArith_Int32::New(space, getStubCode()); + return ICBinaryArith_Int32::New(space, getStubCode(), allowDouble_); } }; }; class ICBinaryArith_StringConcat : public ICStub { friend class ICStubSpace;
--- a/js/src/ion/BaselineInspector.cpp +++ b/js/src/ion/BaselineInspector.cpp @@ -155,16 +155,19 @@ BaselineInspector::expectedResultType(js // returning MIRType_None otherwise. ICStub *stub = monomorphicStub(pc); if (!stub) return MIRType_None; switch (stub->kind()) { case ICStub::BinaryArith_Int32: + if (stub->toBinaryArith_Int32()->allowDouble()) + return MIRType_Double; + return MIRType_Int32; case ICStub::BinaryArith_BooleanWithInt32: case ICStub::UnaryArith_Int32: return MIRType_Int32; case ICStub::BinaryArith_Double: case ICStub::BinaryArith_DoubleWithInt32: case ICStub::UnaryArith_Double: return MIRType_Double; case ICStub::BinaryArith_StringConcat:
--- a/js/src/ion/CodeGenerator.cpp +++ b/js/src/ion/CodeGenerator.cpp @@ -1134,32 +1134,32 @@ CodeGenerator::visitParDump(LParDump *li masm.freeStack(sizeof(Value)); return true; } bool CodeGenerator::visitTypeBarrier(LTypeBarrier *lir) { ValueOperand operand = ToValue(lir, LTypeBarrier::Input); - Register scratch = ToRegister(lir->temp()); + Register scratch = ToTempUnboxRegister(lir->temp()); Label matched, miss; masm.guardTypeSet(operand, lir->mir()->resultTypeSet(), scratch, &matched, &miss); masm.jump(&miss); if (!bailoutFrom(&miss, lir->snapshot())) return false; masm.bind(&matched); return true; } bool CodeGenerator::visitMonitorTypes(LMonitorTypes *lir) { ValueOperand operand = ToValue(lir, LMonitorTypes::Input); - Register scratch = ToRegister(lir->temp()); + Register scratch = ToTempUnboxRegister(lir->temp()); Label matched, miss; masm.guardTypeSet(operand, lir->mir()->typeSet(), scratch, &matched, &miss); masm.jump(&miss); if (!bailoutFrom(&miss, lir->snapshot())) return false; masm.bind(&matched); return true; @@ -1283,17 +1283,17 @@ CodeGenerator::visitPostWriteBarrierV(LP } else { Label tenured; Register objreg = ToRegister(lir->object()); masm.branchPtr(Assembler::Below, objreg, ImmWord(nursery.start()), &tenured); masm.branchPtr(Assembler::Below, objreg, ImmWord(nursery.heapEnd()), ool->rejoin()); masm.bind(&tenured); } - Register valuereg = masm.extractObject(value, ToRegister(lir->temp())); + Register valuereg = masm.extractObject(value, ToTempUnboxRegister(lir->temp())); masm.branchPtr(Assembler::Below, valuereg, ImmWord(nursery.start()), ool->rejoin()); masm.branchPtr(Assembler::Below, valuereg, ImmWord(nursery.heapEnd()), ool->entry()); masm.bind(ool->rejoin()); #endif return true; } @@ -3539,26 +3539,27 @@ bool CodeGenerator::visitCompareStrictS(LCompareStrictS *lir) { JSOp op = lir->mir()->jsop(); JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE); const ValueOperand leftV = ToValue(lir, LCompareStrictS::Lhs); Register right = ToRegister(lir->right()); Register output = ToRegister(lir->output()); - Register temp = ToRegister(lir->temp0()); + Register temp = ToRegister(lir->temp()); + Register tempToUnbox = ToTempUnboxRegister(lir->tempToUnbox()); Label string, done; masm.branchTestString(Assembler::Equal, leftV, &string); masm.move32(Imm32(op == JSOP_STRICTNE), output); masm.jump(&done); masm.bind(&string); - Register left = masm.extractString(leftV, ToRegister(lir->temp1())); + Register left = masm.extractString(leftV, tempToUnbox); if (!emitCompareS(lir, op, left, right, output, temp)) return false; masm.bind(&done); return true; } @@ -3705,19 +3706,19 @@ CodeGenerator::visitIsNullOrLikeUndefine masm.branchTestNull(Assembler::Equal, tag, nullOrLikeUndefined); masm.branchTestUndefined(Assembler::Equal, tag, nullOrLikeUndefined); if (ool) { // Check whether it's a truthy object or a falsy object that emulates // undefined. masm.branchTestObject(Assembler::NotEqual, tag, notNullOrLikeUndefined); - Register objreg = masm.extractObject(value, ToRegister(lir->temp0())); + Register objreg = masm.extractObject(value, ToTempUnboxRegister(lir->tempToUnbox())); testObjectTruthy(objreg, notNullOrLikeUndefined, nullOrLikeUndefined, - ToRegister(lir->temp1()), ool); + ToRegister(lir->temp()), ool); } Label done; // It's not null or undefined, and if it's an object it doesn't // emulate undefined, so it's not like undefined. masm.bind(notNullOrLikeUndefined); masm.move32(Imm32(op == JSOP_NE), output); @@ -3784,18 +3785,18 @@ CodeGenerator::visitIsNullOrLikeUndefine masm.branchTestNull(Assembler::Equal, tag, ifTrueLabel); masm.branchTestUndefined(Assembler::Equal, tag, ifTrueLabel); if (ool) { masm.branchTestObject(Assembler::NotEqual, tag, ifFalseLabel); // Objects that emulate undefined are loosely equal to null/undefined. - Register objreg = masm.extractObject(value, ToRegister(lir->temp0())); - testObjectTruthy(objreg, ifFalseLabel, ifTrueLabel, ToRegister(lir->temp1()), ool); + Register objreg = masm.extractObject(value, ToTempUnboxRegister(lir->tempToUnbox())); + testObjectTruthy(objreg, ifFalseLabel, ifTrueLabel, ToRegister(lir->temp()), ool); } else { masm.jump(ifFalseLabel); } return true; } JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE);
--- a/js/src/ion/IonBuilder.cpp +++ b/js/src/ion/IonBuilder.cpp @@ -3259,17 +3259,17 @@ IonBuilder::jsop_bitop(JSOp op) break; default: JS_NOT_REACHED("unexpected bitop"); return false; } current->add(ins); - ins->infer(); + ins->infer(inspector, pc); current->push(ins); if (ins->isEffectful() && !resumeAfter(ins)) return false; return true; } @@ -6464,17 +6464,17 @@ IonBuilder::convertShiftToMaskForStaticT if (!value.isInt32() || uint32_t(value.toInt32()) != TypedArrayShift(viewType)) return NULL; // Instead of shifting, mask off the low bits of the index so that // a non-scaled access on the typed array can be performed. MConstant *mask = MConstant::New(Int32Value(~((1 << value.toInt32()) - 1))); MBitAnd *ptr = MBitAnd::New(id->getOperand(0), mask); - ptr->infer(); + ptr->infer(NULL, NULL); JS_ASSERT(!ptr->isEffectful()); current->add(mask); current->add(ptr); return ptr; } @@ -6602,25 +6602,51 @@ IonBuilder::jsop_getelem_typed(int array current->add(load); current->push(load); // Note: we can ignore the type barrier here, we know the type must // be valid and unbarriered. load->setResultType(knownType); return true; } else { + // We need a type barrier if the array's element type has never been + // observed (we've only read out-of-bounds values). Note that for + // Uint32Array, we only check for int32: if allowDouble is false we + // will bailout when we read a double. + bool needsBarrier = true; + switch (arrayType) { + case TypedArray::TYPE_INT8: + case TypedArray::TYPE_UINT8: + case TypedArray::TYPE_UINT8_CLAMPED: + case TypedArray::TYPE_INT16: + case TypedArray::TYPE_UINT16: + case TypedArray::TYPE_INT32: + case TypedArray::TYPE_UINT32: + if (types->hasType(types::Type::Int32Type())) + needsBarrier = false; + break; + case TypedArray::TYPE_FLOAT32: + case TypedArray::TYPE_FLOAT64: + if (allowDouble) + needsBarrier = false; + break; + default: + JS_NOT_REACHED("Unknown typed array type"); + return false; + } + // Assume we will read out-of-bound values. In this case the // bounds check will be part of the instruction, and the instruction // will always return a Value. MLoadTypedArrayElementHole *load = MLoadTypedArrayElementHole::New(obj, id, arrayType, allowDouble); current->add(load); current->push(load); - return resumeAfter(load) && pushTypeBarrier(load, types, false); + return resumeAfter(load) && pushTypeBarrier(load, types, needsBarrier); } } bool IonBuilder::jsop_getelem_string() { MDefinition *id = current->pop(); MDefinition *str = current->pop();
--- a/js/src/ion/IonMacroAssembler.cpp +++ b/js/src/ion/IonMacroAssembler.cpp @@ -78,16 +78,17 @@ MacroAssembler::guardTypeSet(const Sourc if (types->hasType(types::Type::NullType())) branchTestNull(Equal, tag, matched); if (types->hasType(types::Type::MagicArgType())) branchTestMagic(Equal, tag, matched); if (types->hasType(types::Type::AnyObjectType())) { branchTestObject(Equal, tag, matched); } else if (types->getObjectCount()) { + JS_ASSERT(scratch != InvalidReg); branchTestObject(NotEqual, tag, miss); Register obj = extractObject(address, scratch); unsigned count = types->getObjectCount(); for (unsigned i = 0; i < count; i++) { if (JSObject *object = types->getSingleObject(i)) branchPtr(Equal, obj, ImmGCPtr(object), matched); }
--- a/js/src/ion/LIR-Common.h +++ b/js/src/ion/LIR-Common.h @@ -1556,20 +1556,20 @@ class LCompareStrictS : public LInstruct setTemp(1, temp1); } static const size_t Lhs = 0; const LAllocation *right() { return getOperand(BOX_PIECES); } - const LDefinition *temp0() { + const LDefinition *temp() { return getTemp(0); } - const LDefinition *temp1() { + const LDefinition *tempToUnbox() { return getTemp(1); } MCompare *mir() { return mir_->toCompare(); } }; // Used for strict-equality comparisons where one side is a boolean @@ -1675,65 +1675,65 @@ class LCompareVM : public LCallInstructi } }; class LIsNullOrLikeUndefined : public LInstructionHelper<1, BOX_PIECES, 2> { public: LIR_HEADER(IsNullOrLikeUndefined) - LIsNullOrLikeUndefined(const LDefinition &temp0, const LDefinition &temp1) + LIsNullOrLikeUndefined(const LDefinition &temp, const LDefinition &tempToUnbox) { - setTemp(0, temp0); - setTemp(1, temp1); + setTemp(0, temp); + setTemp(1, tempToUnbox); } static const size_t Value = 0; MCompare *mir() { return mir_->toCompare(); } - const LDefinition *temp0() { + const LDefinition *temp() { return getTemp(0); } - const LDefinition *temp1() { + const LDefinition *tempToUnbox() { return getTemp(1); } }; class LIsNullOrLikeUndefinedAndBranch : public LControlInstructionHelper<2, BOX_PIECES, 2> { public: LIR_HEADER(IsNullOrLikeUndefinedAndBranch) - LIsNullOrLikeUndefinedAndBranch(MBasicBlock *ifTrue, MBasicBlock *ifFalse, const LDefinition &temp0, const LDefinition &temp1) + LIsNullOrLikeUndefinedAndBranch(MBasicBlock *ifTrue, MBasicBlock *ifFalse, const LDefinition &temp, const LDefinition &tempToUnbox) { setSuccessor(0, ifTrue); setSuccessor(1, ifFalse); - setTemp(0, temp0); - setTemp(1, temp1); + setTemp(0, temp); + setTemp(1, tempToUnbox); } static const size_t Value = 0; MBasicBlock *ifTrue() const { return getSuccessor(0); } MBasicBlock *ifFalse() const { return getSuccessor(1); } MCompare *mir() { return mir_->toCompare(); } - const LDefinition *temp0() { + const LDefinition *temp() { return getTemp(0); } - const LDefinition *temp1() { + const LDefinition *tempToUnbox() { return getTemp(1); } }; // Takes an object and tests whether it emulates |undefined|, as determined by // the JSCLASS_EMULATES_UNDEFINED class flag on unwrapped objects. See also // js::EmulatesUndefined. class LEmulatesUndefined : public LInstructionHelper<1, 1, 0>
--- a/js/src/ion/Lowering.cpp +++ b/js/src/ion/Lowering.cpp @@ -601,27 +601,27 @@ LIRGenerator::visitTest(MTest *test) MOZ_ASSERT(comp->operandMightEmulateUndefined(), "MCompare::tryFold should handle the never-emulates-undefined case"); LEmulatesUndefinedAndBranch *lir = new LEmulatesUndefinedAndBranch(useRegister(left), ifTrue, ifFalse, temp()); return add(lir, comp); } - LDefinition temp0, temp1; + LDefinition tmp, tmpToUnbox; if (comp->operandMightEmulateUndefined()) { - temp0 = temp(); - temp1 = temp(); + tmp = temp(); + tmpToUnbox = tempToUnbox(); } else { - temp0 = LDefinition::BogusTemp(); - temp1 = LDefinition::BogusTemp(); + tmp = LDefinition::BogusTemp(); + tmpToUnbox = LDefinition::BogusTemp(); } LIsNullOrLikeUndefinedAndBranch *lir = - new LIsNullOrLikeUndefinedAndBranch(ifTrue, ifFalse, temp0, temp1); + new LIsNullOrLikeUndefinedAndBranch(ifTrue, ifFalse, tmp, tmpToUnbox); if (!useBox(lir, LIsNullOrLikeUndefinedAndBranch::Value, left)) return false; return add(lir, comp); } // Compare and branch booleans. if (comp->compareType() == MCompare::Compare_Boolean) { JS_ASSERT(left->type() == MIRType_Value); @@ -744,17 +744,17 @@ LIRGenerator::visitCompare(MCompare *com return assignSafepoint(lir, comp); } // Strict compare between value and string if (comp->compareType() == MCompare::Compare_StrictString) { JS_ASSERT(left->type() == MIRType_Value); JS_ASSERT(right->type() == MIRType_String); - LCompareStrictS *lir = new LCompareStrictS(useRegister(right), temp(), temp()); + LCompareStrictS *lir = new LCompareStrictS(useRegister(right), temp(), tempToUnbox()); if (!useBox(lir, LCompareStrictS::Lhs, left)) return false; if (!define(lir, comp)) return false; return assignSafepoint(lir, comp); } // Unknown/unspecialized compare use a VM call. @@ -780,26 +780,26 @@ LIRGenerator::visitCompare(MCompare *com { if (left->type() == MIRType_Object) { MOZ_ASSERT(comp->operandMightEmulateUndefined(), "MCompare::tryFold should have folded this away"); return define(new LEmulatesUndefined(useRegister(left)), comp); } - LDefinition temp0, temp1; + LDefinition tmp, tmpToUnbox; if (comp->operandMightEmulateUndefined()) { - temp0 = temp(); - temp1 = temp(); + tmp = temp(); + tmpToUnbox = tempToUnbox(); } else { - temp0 = LDefinition::BogusTemp(); - temp1 = LDefinition::BogusTemp(); + tmp = LDefinition::BogusTemp(); + tmpToUnbox = LDefinition::BogusTemp(); } - LIsNullOrLikeUndefined *lir = new LIsNullOrLikeUndefined(temp0, temp1); + LIsNullOrLikeUndefined *lir = new LIsNullOrLikeUndefined(tmp, tmpToUnbox); if (!useBox(lir, LIsNullOrLikeUndefined::Value, left)) return false; return define(lir, comp); } // Compare booleans. if (comp->compareType() == MCompare::Compare_Boolean) { JS_ASSERT(left->type() == MIRType_Value); @@ -1727,30 +1727,40 @@ LIRGenerator::visitStoreSlot(MStoreSlot return true; } bool LIRGenerator::visitTypeBarrier(MTypeBarrier *ins) { // Requesting a non-GC pointer is safe here since we never re-enter C++ // from inside a type barrier test. - LTypeBarrier *barrier = new LTypeBarrier(temp()); + + const types::StackTypeSet *types = ins->resultTypeSet(); + bool needTemp = !types->unknownObject() && types->getObjectCount() > 0; + LDefinition tmp = needTemp ? temp() : tempToUnbox(); + + LTypeBarrier *barrier = new LTypeBarrier(tmp); if (!useBox(barrier, LTypeBarrier::Input, ins->input())) return false; if (!assignSnapshot(barrier, ins->bailoutKind())) return false; return redefine(ins, ins->input()) && add(barrier, ins); } bool LIRGenerator::visitMonitorTypes(MMonitorTypes *ins) { // Requesting a non-GC pointer is safe here since we never re-enter C++ // from inside a type check. - LMonitorTypes *lir = new LMonitorTypes(temp()); + + const types::StackTypeSet *types = ins->typeSet(); + bool needTemp = !types->unknownObject() && types->getObjectCount() > 0; + LDefinition tmp = needTemp ? temp() : tempToUnbox(); + + LMonitorTypes *lir = new LMonitorTypes(tmp); if (!useBox(lir, LMonitorTypes::Input, ins->input())) return false; return assignSnapshot(lir, Bailout_Normal) && add(lir, ins); } bool LIRGenerator::visitPostWriteBarrier(MPostWriteBarrier *ins) { @@ -1758,17 +1768,17 @@ LIRGenerator::visitPostWriteBarrier(MPos switch (ins->value()->type()) { case MIRType_Object: { LPostWriteBarrierO *lir = new LPostWriteBarrierO(useRegisterOrConstant(ins->object()), useRegister(ins->value())); return add(lir, ins) && assignSafepoint(lir, ins); } case MIRType_Value: { LPostWriteBarrierV *lir = - new LPostWriteBarrierV(useRegisterOrConstant(ins->object()), temp()); + new LPostWriteBarrierV(useRegisterOrConstant(ins->object()), tempToUnbox()); if (!useBox(lir, LPostWriteBarrierV::Input, ins->value())) return false; return add(lir, ins) && assignSafepoint(lir, ins); } default: // Currently, only objects can be in the nursery. Other instruction // types cannot hold nursery pointers. return true;
--- a/js/src/ion/MIR.cpp +++ b/js/src/ion/MIR.cpp @@ -914,17 +914,17 @@ MBinaryBitwiseInstruction::foldUnnecessa if (EqualValues(false, lhs, rhs)) return foldIfEqual(); return this; } void -MBinaryBitwiseInstruction::infer() +MBinaryBitwiseInstruction::infer(BaselineInspector *, jsbytecode *) { if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Object)) { specialization_ = MIRType_None; } else { specialization_ = MIRType_Int32; setCommutative(); } } @@ -933,40 +933,41 @@ void MBinaryBitwiseInstruction::specializeForAsmJS() { specialization_ = MIRType_Int32; JS_ASSERT(type() == MIRType_Int32); setCommutative(); } void -MShiftInstruction::infer() +MShiftInstruction::infer(BaselineInspector *, jsbytecode *) { if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Object)) specialization_ = MIRType_None; else specialization_ = MIRType_Int32; } void -MUrsh::infer() +MUrsh::infer(BaselineInspector *inspector, jsbytecode *pc) { if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Object)) { specialization_ = MIRType_None; setResultType(MIRType_Value); return; } - if (type() == MIRType_Int32) { - specialization_ = MIRType_Int32; + if (inspector->expectedResultType(pc) == MIRType_Double) { + specialization_ = MIRType_Double; + setResultType(MIRType_Double); return; } - specialization_ = MIRType_Double; - setResultType(MIRType_Double); + specialization_ = MIRType_Int32; + setResultType(MIRType_Int32); } static inline bool NeedNegativeZeroCheck(MDefinition *def) { // Test if all uses have the same semantics for -0 and 0 for (MUseIterator use = def->usesBegin(); use != def->usesEnd(); use++) { if (use->consumer()->isResumePoint())
--- a/js/src/ion/MIR.h +++ b/js/src/ion/MIR.h @@ -2711,17 +2711,17 @@ class MBinaryBitwiseInstruction return this; } MDefinition *foldsTo(bool useValueNumbers); MDefinition *foldUnnecessaryBitop(); virtual MDefinition *foldIfZero(size_t operand) = 0; virtual MDefinition *foldIfNegOne(size_t operand) = 0; virtual MDefinition *foldIfEqual() = 0; - virtual void infer(); + virtual void infer(BaselineInspector *inspector, jsbytecode *pc); bool congruentTo(MDefinition *const &ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { if (specialization_ >= MIRType_Object) return AliasSet::Store(AliasSet::Any); return AliasSet::None(); @@ -2807,17 +2807,17 @@ class MShiftInstruction public: MDefinition *foldIfNegOne(size_t operand) { return this; } MDefinition *foldIfEqual() { return this; } - virtual void infer(); + virtual void infer(BaselineInspector *inspector, jsbytecode *pc); }; class MLsh : public MShiftInstruction { MLsh(MDefinition *left, MDefinition *right) : MShiftInstruction(left, right) { } @@ -2871,17 +2871,17 @@ class MUrsh : public MShiftInstruction MDefinition *foldIfZero(size_t operand) { // 0 >>> x => 0 if (operand == 0) return getOperand(0); return this; } - void infer(); + void infer(BaselineInspector *inspector, jsbytecode *pc); bool canOverflow() { // solution is only negative when lhs < 0 and rhs & 0x1f == 0 MDefinition *lhs = getOperand(0); MDefinition *rhs = getOperand(1); if (lhs->isConstant()) { Value lhsv = lhs->toConstant()->value();
--- a/js/src/ion/arm/Lowering-arm.h +++ b/js/src/ion/arm/Lowering-arm.h @@ -21,16 +21,20 @@ class LIRGeneratorARM : public LIRGenera protected: // Adds a box input to an instruction, setting operand |n| to the type and // |n+1| to the payload. bool useBox(LInstruction *lir, size_t n, MDefinition *mir, LUse::Policy policy = LUse::REGISTER, bool useAtStart = false); bool useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, Register reg2); + inline LDefinition tempToUnbox() { + return LDefinition::BogusTemp(); + } + void lowerUntypedPhiInput(MPhi *phi, uint32_t inputPosition, LBlock *block, size_t lirIndex); bool defineUntypedPhi(MPhi *phi, size_t lirIndex); bool lowerForShift(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, MDefinition *lhs, MDefinition *rhs); bool lowerUrshD(MUrsh *mir); bool lowerForALU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir, MDefinition *input);
--- a/js/src/ion/shared/CodeGenerator-shared-inl.h +++ b/js/src/ion/shared/CodeGenerator-shared-inl.h @@ -41,16 +41,24 @@ ToRegister(const LAllocation *a) static inline Register ToRegister(const LDefinition *def) { return ToRegister(*def->output()); } static inline Register +ToTempUnboxRegister(const LDefinition *def) +{ + if (def->isBogusTemp()) + return InvalidReg; + return ToRegister(def); +} + +static inline Register ToRegisterOrInvalid(const LAllocation *a) { return a ? ToRegister(*a) : InvalidReg; } static inline FloatRegister ToFloatRegister(const LAllocation &a) {
--- a/js/src/ion/x64/Lowering-x64.h +++ b/js/src/ion/x64/Lowering-x64.h @@ -23,16 +23,20 @@ class LIRGeneratorX64 : public LIRGenera void lowerUntypedPhiInput(MPhi *phi, uint32_t inputPosition, LBlock *block, size_t lirIndex); bool defineUntypedPhi(MPhi *phi, size_t lirIndex); // Adds a use at operand |n| of a value-typed insturction. bool useBox(LInstruction *lir, size_t n, MDefinition *mir, LUse::Policy policy = LUse::REGISTER, bool useAtStart = false); bool useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, Register); + inline LDefinition tempToUnbox() { + return temp(); + } + LGetPropertyCacheT *newLGetPropertyCacheT(MGetPropertyCache *ins); public: bool visitBox(MBox *box); bool visitUnbox(MUnbox *unbox); bool visitReturn(MReturn *ret); bool visitStoreTypedArrayElement(MStoreTypedArrayElement *ins); bool visitStoreTypedArrayElementHole(MStoreTypedArrayElementHole *ins);
--- a/js/src/ion/x86/Lowering-x86.h +++ b/js/src/ion/x86/Lowering-x86.h @@ -21,16 +21,20 @@ class LIRGeneratorX86 : public LIRGenera protected: // Adds a box input to an instruction, setting operand |n| to the type and // |n+1| to the payload. bool useBox(LInstruction *lir, size_t n, MDefinition *mir, LUse::Policy policy = LUse::REGISTER, bool useAtStart = false); bool useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, Register reg2); + inline LDefinition tempToUnbox() { + return LDefinition::BogusTemp(); + } + void lowerUntypedPhiInput(MPhi *phi, uint32_t inputPosition, LBlock *block, size_t lirIndex); bool defineUntypedPhi(MPhi *phi, size_t lirIndex); LGetPropertyCacheT *newLGetPropertyCacheT(MGetPropertyCache *ins); public: bool visitBox(MBox *box); bool visitUnbox(MUnbox *unbox);
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/ion/bug882323.js @@ -0,0 +1,23 @@ +var ints = new Int8Array(16); +ints[0] = 42; +function intElementAt(index) { + return ints[index]; +} +assertEq(intElementAt(16), undefined); +assertEq(intElementAt(0), 42); + +var floats = new Float64Array(16); +floats[0] = 3.14; +function floatElementAt(index) { + return floats[index]; +} +assertEq(floatElementAt(16), undefined); +assertEq(floatElementAt(0), 3.14); + +var uints = new Uint32Array(16); +uints[0] = 123; +function uintElementAt(index) { + return uints[index]; +} +assertEq(uintElementAt(16), undefined); +assertEq(uintElementAt(0), 123);
--- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -26,16 +26,21 @@ ((uintptr_t)(sp) < (limit)+(tolerance)) #else # define JS_CHECK_STACK_SIZE_WITH_TOLERANCE(limit, sp, tolerance) \ ((uintptr_t)(sp) > (limit)-(tolerance)) #endif #define JS_CHECK_STACK_SIZE(limit, lval) JS_CHECK_STACK_SIZE_WITH_TOLERANCE(limit, lval, 0) +namespace JS { +template <class T> +class Heap; +} /* namespace JS */ + extern JS_FRIEND_API(void) JS_SetGrayGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data); extern JS_FRIEND_API(JSString *) JS_GetAnonymousString(JSRuntime *rt); extern JS_FRIEND_API(JSObject *) JS_FindCompilationScope(JSContext *cx, JSObject *obj); @@ -885,17 +890,17 @@ NukeCrossCompartmentWrappers(JSContext* */ struct ExpandoAndGeneration { ExpandoAndGeneration() : expando(UndefinedValue()), generation(0) {} - Value expando; + JS::Heap<JS::Value> expando; uint32_t generation; }; typedef enum DOMProxyShadowsResult { ShadowCheckFailed, Shadows, DoesntShadow, DoesntShadowUnique
--- a/js/src/vm/Stack-inl.h +++ b/js/src/vm/Stack-inl.h @@ -331,17 +331,17 @@ ContextStack::popInlineFrame(FrameRegs & inline JSScript * ContextStack::currentScript(jsbytecode **ppc, MaybeAllowCrossCompartment allowCrossCompartment) const { if (ppc) *ppc = NULL; Activation *act = cx_->mainThread().activation(); - while (act && act->cx() != cx_) + while (act && (act->cx() != cx_ || !act->isActive())) act = act->prev(); if (!act) return NULL; JS_ASSERT(act->cx() == cx_); #ifdef JS_ION
--- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -498,30 +498,30 @@ void XPCJSRuntime::TraceGrayJS(JSTracer* XPCJSRuntime* self = (XPCJSRuntime*)data; // Mark these roots as gray so the CC can walk them later. self->TraceXPConnectRoots(trc); } struct JsGcTracer : public TraceCallbacks { - virtual void Trace(JS::Value *p, const char *name, void *closure) const MOZ_OVERRIDE { - JS_CallValueTracer(static_cast<JSTracer*>(closure), p, name); + virtual void Trace(JS::Heap<JS::Value> *p, const char *name, void *closure) const MOZ_OVERRIDE { + JS_CallHeapValueTracer(static_cast<JSTracer*>(closure), p, name); } - virtual void Trace(jsid *p, const char *name, void *closure) const MOZ_OVERRIDE { - JS_CallIdTracer(static_cast<JSTracer*>(closure), p, name); + virtual void Trace(JS::Heap<jsid> *p, const char *name, void *closure) const MOZ_OVERRIDE { + JS_CallHeapIdTracer(static_cast<JSTracer*>(closure), p, name); } - virtual void Trace(JSObject **p, const char *name, void *closure) const MOZ_OVERRIDE { - JS_CallObjectTracer(static_cast<JSTracer*>(closure), p, name); + virtual void Trace(JS::Heap<JSObject *> *p, const char *name, void *closure) const MOZ_OVERRIDE { + JS_CallHeapObjectTracer(static_cast<JSTracer*>(closure), p, name); } - virtual void Trace(JSString **p, const char *name, void *closure) const MOZ_OVERRIDE { - JS_CallStringTracer(static_cast<JSTracer*>(closure), p, name); + virtual void Trace(JS::Heap<JSString *> *p, const char *name, void *closure) const MOZ_OVERRIDE { + JS_CallHeapStringTracer(static_cast<JSTracer*>(closure), p, name); } - virtual void Trace(JSScript **p, const char *name, void *closure) const MOZ_OVERRIDE { - JS_CallScriptTracer(static_cast<JSTracer*>(closure), p, name); + virtual void Trace(JS::Heap<JSScript *> *p, const char *name, void *closure) const MOZ_OVERRIDE { + JS_CallHeapScriptTracer(static_cast<JSTracer*>(closure), p, name); } }; static PLDHashOperator TraceJSHolder(void *holder, nsScriptObjectTracer *&tracer, void *arg) { tracer->Trace(holder, JsGcTracer(), arg);
--- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -322,17 +322,18 @@ NS_NewResizerFrame (nsIPresShell* aPresS #endif nsIFrame* NS_NewHTMLScrollFrame (nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot); nsIFrame* -NS_NewXULScrollFrame (nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot); +NS_NewXULScrollFrame (nsIPresShell* aPresShell, nsStyleContext* aContext, + bool aIsRoot, bool aClipAllDescendants); nsIFrame* NS_NewSliderFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); nsIFrame* NS_NewScrollbarFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); nsIFrame* @@ -4111,18 +4112,21 @@ nsCSSFrameConstructor::BeginBuildingScro nsRefPtr<nsStyleContext> contentStyle = aContentStyle; if (!gfxScrollFrame) { // Build a XULScrollFrame when the child is a box, otherwise an // HTMLScrollFrame // XXXbz this is the lone remaining consumer of IsXULDisplayType. // I wonder whether we can eliminate that somehow. - if (IsXULDisplayType(aContentStyle->StyleDisplay())) { - gfxScrollFrame = NS_NewXULScrollFrame(mPresShell, contentStyle, aIsRoot); + const nsStyleDisplay* displayStyle = aContentStyle->StyleDisplay(); + if (IsXULDisplayType(displayStyle)) { + gfxScrollFrame = NS_NewXULScrollFrame(mPresShell, contentStyle, aIsRoot, + displayStyle->mDisplay == NS_STYLE_DISPLAY_STACK || + displayStyle->mDisplay == NS_STYLE_DISPLAY_INLINE_STACK); } else { gfxScrollFrame = NS_NewHTMLScrollFrame(mPresShell, contentStyle, aIsRoot); } InitAndRestoreFrame(aState, aContent, aParentFrame, gfxScrollFrame); } // if there are any anonymous children for the scroll frame, create
--- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -593,18 +593,20 @@ void nsDisplayListBuilder::MarkOutOfFlow } if (mHasDisplayPort && IsFixedFrame(aFrame)) { dirty = overflowRect; } if (!dirty.IntersectRect(dirty, overflowRect)) return; - aFrame->Properties().Set(nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), - new OutOfFlowDisplayData(mClipState.GetClipForContainingBlockDescendants(), dirty)); + const DisplayItemClip* clip = mClipState.GetClipForContainingBlockDescendants(); + OutOfFlowDisplayData* data = clip ? new OutOfFlowDisplayData(*clip, dirty) + : new OutOfFlowDisplayData(dirty); + aFrame->Properties().Set(nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data); MarkFrameForDisplay(aFrame, aDirtyFrame); } static void UnmarkFrameForDisplay(nsIFrame* aFrame) { nsPresContext* presContext = aFrame->PresContext(); presContext->PropertyTable()-> Delete(aFrame, nsDisplayListBuilder::OutOfFlowDisplayDataProperty()); @@ -803,24 +805,30 @@ void nsDisplayListBuilder::LeavePresShell(nsIFrame* aReferenceFrame, const nsRect& aDirtyRect) { if (CurrentPresShellState()->mPresShell != aReferenceFrame->PresContext()->PresShell()) { // Must have not allocated a state for this presshell, presumably due // to OOM. return; } + ResetMarkedFramesForDisplayList(); + mPresShellStates.SetLength(mPresShellStates.Length() - 1); +} + +void +nsDisplayListBuilder::ResetMarkedFramesForDisplayList() +{ // Unmark and pop off the frames marked for display in this pres shell. uint32_t firstFrameForShell = CurrentPresShellState()->mFirstFrameMarkedForDisplay; for (uint32_t i = firstFrameForShell; i < mFramesMarkedForDisplay.Length(); ++i) { UnmarkFrameForDisplay(mFramesMarkedForDisplay[i]); } mFramesMarkedForDisplay.SetLength(firstFrameForShell); - mPresShellStates.SetLength(mPresShellStates.Length() - 1); } void nsDisplayListBuilder::MarkFramesForDisplayList(nsIFrame* aDirtyFrame, const nsFrameList& aFrames, const nsRect& aDirtyRect) { for (nsFrameList::Enumerator e(aFrames); !e.AtEnd(); e.Next()) { mFramesMarkedForDisplay.AppendElement(e.get()); @@ -1194,28 +1202,31 @@ void nsDisplayList::PaintForFrame(nsDisp nsIntRegion invalid; if (props) { invalid = props->ComputeDifferences(root, computeInvalidFunc); } else if (widgetTransaction) { LayerProperties::ClearInvalidations(root); } + bool shouldInvalidate = layerManager->NeedsWidgetInvalidation(); if (view) { if (props) { if (!invalid.IsEmpty()) { nsIntRect bounds = invalid.GetBounds(); nsRect rect(presContext->DevPixelsToAppUnits(bounds.x), presContext->DevPixelsToAppUnits(bounds.y), presContext->DevPixelsToAppUnits(bounds.width), presContext->DevPixelsToAppUnits(bounds.height)); - view->GetViewManager()->InvalidateViewNoSuppression(view, rect); + if (shouldInvalidate) { + view->GetViewManager()->InvalidateViewNoSuppression(view, rect); + } presContext->NotifyInvalidation(bounds, 0); } - } else { + } else if (shouldInvalidate) { view->GetViewManager()->InvalidateView(view); } } if (aFlags & PAINT_FLUSH_LAYERS) { FrameLayerBuilder::InvalidateAllLayers(layerManager); }
--- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -330,16 +330,24 @@ public: nsCaret* GetCaret(); /** * Notify the display list builder that we're entering a presshell. * aReferenceFrame should be a frame in the new presshell and aDirtyRect * should be the current dirty rect in aReferenceFrame's coordinate space. */ void EnterPresShell(nsIFrame* aReferenceFrame, const nsRect& aDirtyRect); /** + * For print-preview documents, we sometimes need to build display items for + * the same frames multiple times in the same presentation, with different + * clipping. Between each such batch of items, call + * ResetMarkedFramesForDisplayList to make sure that the results of + * MarkFramesForDisplayList do not carry over between batches. + */ + void ResetMarkedFramesForDisplayList(); + /** * Notify the display list builder that we're leaving a presshell. */ void LeavePresShell(nsIFrame* aReferenceFrame, const nsRect& aDirtyRect); /** * Returns true if we're currently building a display list that's * directly or indirectly under an nsDisplayTransform. */ @@ -561,22 +569,25 @@ public: bool mOldValue; }; // Helpers for tables nsDisplayTableItem* GetCurrentTableItem() { return mCurrentTableItem; } void SetCurrentTableItem(nsDisplayTableItem* aTableItem) { mCurrentTableItem = aTableItem; } struct OutOfFlowDisplayData { - OutOfFlowDisplayData(const DisplayItemClip* aContainingBlockClip, + OutOfFlowDisplayData(const DisplayItemClip& aContainingBlockClip, const nsRect &aDirtyRect) : mContainingBlockClip(aContainingBlockClip) , mDirtyRect(aDirtyRect) {} - const DisplayItemClip* mContainingBlockClip; + OutOfFlowDisplayData(const nsRect &aDirtyRect) + : mDirtyRect(aDirtyRect) + {} + DisplayItemClip mContainingBlockClip; nsRect mDirtyRect; }; static void DestroyOutOfFlowDisplayData(void* aPropertyValue) { delete static_cast<OutOfFlowDisplayData*>(aPropertyValue); } NS_DECLARE_FRAME_PROPERTY(OutOfFlowDisplayDataProperty, DestroyOutOfFlowDisplayData)
--- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -1536,22 +1536,16 @@ nsDocumentViewer::Destroy() // Remove our root view from the view hierarchy. if (mPresShell) { nsViewManager *vm = mPresShell->GetViewManager(); if (vm) { nsView *rootView = vm->GetRootView(); if (rootView) { - // The invalidate that removing this view causes is dropped because - // the Freeze call above sets painting to be suppressed for our - // document. So we do it ourselves and make it happen. - vm->InvalidateViewNoSuppression(rootView, - rootView->GetBounds() - rootView->GetPosition()); - nsView *rootViewParent = rootView->GetParent(); if (rootViewParent) { nsViewManager *parentVM = rootViewParent->GetViewManager(); if (parentVM) { parentVM->RemoveChild(rootView); } } }
--- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -5484,27 +5484,32 @@ PresShell::Paint(nsView* aViewToP NS_ASSERTION(aViewToPaint, "null view"); MOZ_ASSERT(!mImageVisibilityVisited, "should have been cleared"); if (!mIsActive || mIsZombie) { return; } - nsAutoNotifyDidPaint notifyDidPaint(this, aFlags); - nsPresContext* presContext = GetPresContext(); AUTO_LAYOUT_PHASE_ENTRY_POINT(presContext, Paint); nsIFrame* frame = aViewToPaint->GetFrame(); bool isRetainingManager; LayerManager* layerManager = aViewToPaint->GetWidget()->GetLayerManager(&isRetainingManager); NS_ASSERTION(layerManager, "Must be in paint event"); + bool shouldInvalidate = layerManager->NeedsWidgetInvalidation(); + + uint32_t didPaintFlags = aFlags; + if (!shouldInvalidate) { + didPaintFlags |= PAINT_COMPOSITE; + } + nsAutoNotifyDidPaint notifyDidPaint(this, didPaintFlags); // Whether or not we should set first paint when painting is // suppressed is debatable. For now we'll do it because // B2G relies on first paint to configure the viewport and // we only want to do that when we have real content to paint. // See Bug 798245 if (mIsFirstPaint && !mPaintingSuppressed) { layerManager->SetIsFirstPaint(); @@ -5514,19 +5519,16 @@ PresShell::Paint(nsView* aViewToP if (frame && isRetainingManager) { // Try to do an empty transaction, if the frame tree does not // need to be updated. Do not try to do an empty transaction on // a non-retained layer manager (like the BasicLayerManager that // draws the window title bar on Mac), because a) it won't work // and b) below we don't want to clear NS_FRAME_UPDATE_LAYER_TREE, // that will cause us to forget to update the real layer manager! if (!(aFlags & PAINT_LAYERS)) { - if (layerManager->HasShadowManager() && Compositor::GetBackend() != LAYERS_BASIC) { - return; - } layerManager->BeginTransaction(); if (layerManager->EndEmptyTransaction()) { return; } NS_WARNING("Must complete empty transaction when compositing!"); } else { layerManager->BeginTransaction(); } @@ -5551,20 +5553,22 @@ PresShell::Paint(nsView* aViewToP } if (props) { if (!invalid.IsEmpty()) { nsIntRect bounds = invalid.GetBounds(); nsRect rect(presContext->DevPixelsToAppUnits(bounds.x), presContext->DevPixelsToAppUnits(bounds.y), presContext->DevPixelsToAppUnits(bounds.width), presContext->DevPixelsToAppUnits(bounds.height)); - aViewToPaint->GetViewManager()->InvalidateViewNoSuppression(aViewToPaint, rect); + if (shouldInvalidate) { + aViewToPaint->GetViewManager()->InvalidateViewNoSuppression(aViewToPaint, rect); + } presContext->NotifyInvalidation(bounds, 0); } - } else { + } else if (shouldInvalidate) { aViewToPaint->GetViewManager()->InvalidateView(aViewToPaint); } frame->UpdatePaintCountForPaintedPresShells(); return; } } frame->RemoveStateBits(NS_FRAME_UPDATE_LAYER_TREE);
--- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -2102,17 +2102,17 @@ nsIFrame::BuildDisplayListForChild(nsDis IsRootScrollFrameActive(PresContext()->PresShell()) && !isSVG; nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild(aBuilder, child, pseudoStackingContext, buildFixedPositionItem); DisplayListClipState::AutoClipMultiple clipState(aBuilder); if (savedOutOfFlowData) { clipState.SetClipForContainingBlockDescendants( - savedOutOfFlowData->mContainingBlockClip); + &savedOutOfFlowData->mContainingBlockClip); } // Setup clipping for the parent's overflow:-moz-hidden-unscrollable, // or overflow:hidden on elements that don't support scrolling (and therefore // don't create nsHTML/XULScrollFrame). This clipping needs to not clip // anything directly rendered by the parent, only the rendering of its // children. // Don't use overflowClip to restrict the dirty rect, since some of the
--- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -885,28 +885,32 @@ NS_QUERYFRAME_HEAD(nsHTMLScrollFrame) NS_QUERYFRAME_ENTRY(nsIScrollbarOwner) NS_QUERYFRAME_ENTRY(nsIScrollableFrame) NS_QUERYFRAME_ENTRY(nsIStatefulFrame) NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) //----------nsXULScrollFrame------------------------------------------- nsIFrame* -NS_NewXULScrollFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot) +NS_NewXULScrollFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, + bool aIsRoot, bool aClipAllDescendants) { - return new (aPresShell) nsXULScrollFrame(aPresShell, aContext, aIsRoot); + return new (aPresShell) nsXULScrollFrame(aPresShell, aContext, aIsRoot, + aClipAllDescendants); } NS_IMPL_FRAMEARENA_HELPERS(nsXULScrollFrame) -nsXULScrollFrame::nsXULScrollFrame(nsIPresShell* aShell, nsStyleContext* aContext, bool aIsRoot) +nsXULScrollFrame::nsXULScrollFrame(nsIPresShell* aShell, nsStyleContext* aContext, + bool aIsRoot, bool aClipAllDescendants) : nsBoxFrame(aShell, aContext, aIsRoot), mInner(ALLOW_THIS_IN_INITIALIZER_LIST(this), aIsRoot) { SetLayoutManager(nullptr); + mInner.mClipAllDescendants = aClipAllDescendants; } nsMargin nsGfxScrollFrameInner::GetDesiredScrollbarSizes(nsBoxLayoutState* aState) { NS_ASSERTION(aState && aState->GetRenderingContext(), "Must have rendering context in layout state for size " "computations"); @@ -1498,16 +1502,17 @@ nsGfxScrollFrameInner::nsGfxScrollFrameI , mLastUpdateImagesPos(-1, -1) , mNeverHasVerticalScrollbar(false) , mNeverHasHorizontalScrollbar(false) , mHasVerticalScrollbar(false) , mHasHorizontalScrollbar(false) , mFrameIsUpdatingScrollbar(false) , mDidHistoryRestore(false) , mIsRoot(aIsRoot) + , mClipAllDescendants(aIsRoot) , mSupppressScrollbarUpdate(false) , mSkippedScrollbarLayout(false) , mHadNonInitialReflow(false) , mHorizontalOverflow(false) , mVerticalOverflow(false) , mPostedReflowCallback(false) , mMayHaveDirtyFixedChildren(false) , mUpdateScrollbarAttributes(false) @@ -2193,26 +2198,26 @@ nsGfxScrollFrameInner::BuildDisplayList( } nsDisplayListCollection set; { DisplayListClipState::AutoSaveRestore clipState(aBuilder); if (usingDisplayport) { nsRect clip = displayPort + aBuilder->ToReferenceFrame(mOuter); - if (mIsRoot) { + if (mClipAllDescendants) { clipState.ClipContentDescendants(clip); } else { clipState.ClipContainingBlockDescendants(clip, nullptr); } } else { nsRect clip = mScrollPort + aBuilder->ToReferenceFrame(mOuter); // Our override of GetBorderRadii ensures we never have a radius at // the corners where we have a scrollbar. - if (mIsRoot) { + if (mClipAllDescendants) { #ifdef DEBUG nscoord radii[8]; #endif NS_ASSERTION(!mOuter->GetPaddingBoxBorderRadii(radii), "Roots with radii not supported"); clipState.ClipContentDescendants(clip); } else { nscoord radii[8];
--- a/layout/generic/nsGfxScrollFrame.h +++ b/layout/generic/nsGfxScrollFrame.h @@ -315,16 +315,19 @@ public: bool mNeverHasVerticalScrollbar:1; bool mNeverHasHorizontalScrollbar:1; bool mHasVerticalScrollbar:1; bool mHasHorizontalScrollbar:1; bool mFrameIsUpdatingScrollbar:1; bool mDidHistoryRestore:1; // Is this the scrollframe for the document's viewport? bool mIsRoot:1; + // True if we should clip all descendants, false if we should only clip + // descendants for which we are the containing block. + bool mClipAllDescendants:1; // If true, don't try to layout the scrollbars in Reflow(). This can be // useful if multiple passes are involved, because we don't want to place the // scrollbars at the wrong size. bool mSupppressScrollbarUpdate:1; // If true, we skipped a scrollbar layout due to mSupppressScrollbarUpdate // being set at some point. That means we should lay out scrollbars even if // it might not strictly be needed next time mSupppressScrollbarUpdate is // false. @@ -625,17 +628,18 @@ private: class nsXULScrollFrame : public nsBoxFrame, public nsIScrollableFrame, public nsIAnonymousContentCreator, public nsIStatefulFrame { public: NS_DECL_QUERYFRAME NS_DECL_FRAMEARENA_HELPERS - friend nsIFrame* NS_NewXULScrollFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot); + friend nsIFrame* NS_NewXULScrollFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, + bool aIsRoot, bool aClipAllDescendants); // Called to set the child frames. We typically have three: the scroll area, // the vertical scrollbar, and the horizontal scrollbar. NS_IMETHOD SetInitialChildList(ChildListID aListID, nsFrameList& aChildList) MOZ_OVERRIDE; virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect, @@ -848,17 +852,18 @@ public: return nsBoxFrame::IsFrameOfType(aFlags); } #ifdef DEBUG NS_IMETHOD GetFrameName(nsAString& aResult) const MOZ_OVERRIDE; #endif protected: - nsXULScrollFrame(nsIPresShell* aShell, nsStyleContext* aContext, bool aIsRoot); + nsXULScrollFrame(nsIPresShell* aShell, nsStyleContext* aContext, bool aIsRoot, + bool aClipAllDescendants); void ClampAndSetBounds(nsBoxLayoutState& aState, nsRect& aRect, nsPoint aScrollPosition, bool aRemoveOverflowAreas = false) { /* * For RTL frames, restore the original scrolled position of the right * edge, then subtract the current width to find the physical position.
--- a/layout/generic/nsSimplePageSequence.cpp +++ b/layout/generic/nsSimplePageSequence.cpp @@ -813,16 +813,17 @@ nsSimplePageSequenceFrame::BuildDisplayL // nsDisplayTransform, since they'll be in a different coordinate system. DisplayListClipState::AutoSaveRestore clipState(aBuilder); clipState.Clear(); nsIFrame* child = GetFirstPrincipalChild(); while (child) { child->BuildDisplayListForStackingContext(aBuilder, child->GetVisualOverflowRectRelativeToSelf(), &content); + aBuilder->ResetMarkedFramesForDisplayList(); child = child->GetNextSibling(); } } content.AppendNewToTop(new (aBuilder) nsDisplayTransform(aBuilder, this, &content, ::ComputePageSequenceTransform)); aLists.Content()->AppendToTop(&content);
--- a/layout/reftests/bugs/598726-1.html +++ b/layout/reftests/bugs/598726-1.html @@ -1,10 +1,10 @@ <!DOCTYPE html> -<html class="reftest-wait"> +<html class="reftest-wait reftest-snapshot-all"> <head> <style type="text/css"> input { -webkit-transition: -webkit-transform 200ms ease-in-out; transition: transform 200ms ease-in-out; } input:focus { -webkit-transform: scale(1.05); @@ -26,17 +26,17 @@ i.getClientRects(); i.addEventListener("transitionend", function(aEvent) { if (aEvent.propertyName != 'transform' && aEvent.propertyName != '-webkit-transform') { return; } i.removeEventListener("transitionend", arguments.callee, true); i.getClientRects(); - document.documentElement.removeAttribute("class"); + document.documentElement.setAttribute("class", "reftest-snapshot-all"); }, true); i.blur(); i.getClientRects(); }, true); i.focus(); i.getClientRects(); } </script>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bugs/875060-1-ref.html @@ -0,0 +1,7 @@ +<!DOCTYPE HTML> +<html> +<body> +<div style="display:-moz-inline-stack; width:100px; height:100px; background:yellow"> +</div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bugs/875060-1.html @@ -0,0 +1,8 @@ +<!DOCTYPE HTML> +<html> +<body> +<div style="display:-moz-inline-stack; overflow:hidden; position:relative; width:100px; height:100px;"> + <div style="width:200px; height:200px; background:yellow; position:absolute"></div> +</div> +</body> +</html>
--- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1755,8 +1755,9 @@ fails-if(Android) == 836844-1.html 83684 == 846144-1.html 846144-1-ref.html == 847850-1.html 847850-1-ref.html == 848421-1.html 848421-1-ref.html test-pref(layout.css.flexbox.enabled,true) == 849407-1.html 849407-1-ref.html == 849996-1.html 849996-1-ref.html == 858803-1.html 858803-1-ref.html != 860370.html 860370-notref.html == 871338-1.html 871338-1-ref.html +== 875060-1.html 875060-1-ref.html
--- a/layout/reftests/forms/input/range/reftest.list +++ b/layout/reftests/forms/input/range/reftest.list @@ -16,18 +16,18 @@ default-preferences pref(dom.experimenta # dynamic value changes: == input-value-prop-unthemed.html input-75pct-unthemed-common-ref.html == input-value-prop.html input-75pct-common-ref.html == input-valueAsNumber-prop-unthemed.html input-75pct-unthemed-common-ref.html == input-valueAsNumber-prop.html input-75pct-common-ref.html fails-if(B2G) == input-stepDown-unthemed.html input-75pct-unthemed-common-ref.html fails-if(B2G) == input-stepDown.html input-75pct-common-ref.html -fails-if(B2G) == input-stepUp-unthemed.html input-75pct-unthemed-common-ref.html -fails-if(B2G) == input-stepUp.html input-75pct-common-ref.html +== input-stepUp-unthemed.html input-75pct-unthemed-common-ref.html +== input-stepUp.html input-75pct-common-ref.html # 'direction' property: == input-range-direction-unthemed-1.html input-range-direction-unthemed-1-ref.html # ::-moz-range-progress pseudo-element: fails-if(B2G) == input-range-moz-range-progress-1.html input-range-moz-range-progress-1-ref.html == input-range-moz-range-progress-2.html input-range-moz-range-progress-2-ref.html == input-range-moz-range-progress-3.html input-range-moz-range-progress-3-ref.html
--- a/layout/style/AnimationCommon.cpp +++ b/layout/style/AnimationCommon.cpp @@ -299,17 +299,20 @@ CommonElementAnimationData::CanAnimatePr } } bool enabled = nsLayoutUtils::AreAsyncAnimationsEnabled(); if (!enabled && shouldLog) { nsCString message; message.AppendLiteral("Performance warning: Async animations are disabled"); LogAsyncAnimationFailure(message); } - return enabled && (aFlags & CanAnimate_AllowPartial); + bool propertyAllowed = (aProperty == eCSSProperty_transform) || + (aProperty == eCSSProperty_opacity) || + (aFlags & CanAnimate_AllowPartial); + return enabled && propertyAllowed; } /* static */ void CommonElementAnimationData::LogAsyncAnimationFailure(nsCString& aMessage, const nsIContent* aContent) { if (aContent) { aMessage.AppendLiteral(" [");
--- a/layout/tools/reftest/reftest-content.js +++ b/layout/tools/reftest/reftest-content.js @@ -236,16 +236,24 @@ function shouldWaitForPendingPaints() { function shouldWaitForReftestWaitRemoval(contentRootElement) { // use getAttribute because className works differently in HTML and SVG return contentRootElement && contentRootElement.hasAttribute('class') && contentRootElement.getAttribute('class').split(/\s+/) .indexOf("reftest-wait") != -1; } +function shouldSnapshotWholePage(contentRootElement) { + // use getAttribute because className works differently in HTML and SVG + return contentRootElement && + contentRootElement.hasAttribute('class') && + contentRootElement.getAttribute('class').split(/\s+/) + .indexOf("reftest-snapshot-all") != -1; +} + function getNoPaintElements(contentRootElement) { return contentRootElement.getElementsByClassName('reftest-no-paint'); } // Initial state. When the document has loaded and all MozAfterPaint events and // all explicit paint waits are flushed, we can fire the MozReftestInvalidate // event and move to the next state. const STATE_WAITING_TO_FIRE_INVALIDATE_EVENT = 0; @@ -301,17 +309,17 @@ function WaitForTestEnd(contentRootEleme function AfterPaintListener(event) { LogInfo("AfterPaintListener in " + event.target.document.location.href); if (event.target.document != currentDoc) { // ignore paint events for subframes or old documents in the window. // Invalidation in subframes will cause invalidation in the toplevel document anyway. return; } - SendUpdateCanvasForEvent(event); + SendUpdateCanvasForEvent(event, contentRootElement); // These events are fired immediately after a paint. Don't // confuse ourselves by firing synchronously if we triggered the // paint ourselves. setTimeout(MakeProgress, 0); } function AttrModifiedListener() { LogInfo("AttrModifiedListener fired"); @@ -835,22 +843,34 @@ function SendTestDone(runtimeMs) sendAsyncMessage("reftest:TestDone", { runtimeMs: runtimeMs }); } function roundTo(x, fraction) { return Math.round(x/fraction)*fraction; } -function SendUpdateCanvasForEvent(event) +function SendUpdateCanvasForEvent(event, contentRootElement) { var win = content; var scale = markupDocumentViewer().fullZoom; var rects = [ ]; + if (shouldSnapshotWholePage) { + // See comments in SendInitCanvasWithSnapshot() re: the split + // logic here. + if (!gBrowserIsRemote) { + sendSyncMessage("reftest:UpdateWholeCanvasForInvalidation"); + } else { + SynchronizeForSnapshot(SYNC_ALLOW_DISABLE); + sendAsyncMessage("reftest:UpdateWholeCanvasForInvalidation"); + } + return; + } + var rectList = event.clientRects; LogInfo("SendUpdateCanvasForEvent with " + rectList.length + " rects"); for (var i = 0; i < rectList.length; ++i) { var r = rectList[i]; // Set left/top/right/bottom to "device pixel" boundaries var left = Math.floor(roundTo(r.left*scale, 0.001)); var top = Math.floor(roundTo(r.top*scale, 0.001)); var right = Math.ceil(roundTo(r.right*scale, 0.001));
--- a/layout/tools/reftest/reftest.js +++ b/layout/tools/reftest/reftest.js @@ -1387,16 +1387,28 @@ function UpdateCurrentCanvasForInvalidat ctx.save(); ctx.translate(left, top); DoDrawWindow(ctx, left, top, right - left, bottom - top); ctx.restore(); } } +function UpdateWholeCurrentCanvasForInvalidation() +{ + LogInfo("Updating entire canvas for invalidation"); + + if (!gCurrentCanvas) { + return; + } + + var ctx = gCurrentCanvas.getContext("2d"); + DoDrawWindow(ctx, 0, 0, gCurrentCanvas.width, gCurrentCanvas.height); +} + function RecordResult(testRunTime, errorMsg, scriptResults) { LogInfo("RecordResult fired"); // Keep track of which test was slowest, and how long it took. if (testRunTime > gSlowestTestTime) { gSlowestTestTime = testRunTime; gSlowestTestURL = gCurrentURL; @@ -1786,16 +1798,20 @@ function RegisterMessageListenersAndLoad "reftest:TestDone", function (m) { RecvTestDone(m.json.runtimeMs); } ); gBrowserMessageManager.addMessageListener( "reftest:UpdateCanvasForInvalidation", function (m) { RecvUpdateCanvasForInvalidation(m.json.rects); } ); gBrowserMessageManager.addMessageListener( + "reftest:UpdateWholeCanvasForInvalidation", + function (m) { RecvUpdateWholeCanvasForInvalidation(); } + ); + gBrowserMessageManager.addMessageListener( "reftest:ExpectProcessCrash", function (m) { RecvExpectProcessCrash(); } ); gBrowserMessageManager.addMessageListener( "reftest:EnableAsyncScroll", function (m) { SetAsyncScroll(true); } ); @@ -1865,16 +1881,21 @@ function RecvTestDone(runtimeMs) RecordResult(runtimeMs, '', [ ]); } function RecvUpdateCanvasForInvalidation(rects) { UpdateCurrentCanvasForInvalidation(rects); } +function RecvUpdateWholeCanvasForInvalidation() +{ + UpdateWholeCurrentCanvasForInvalidation(); +} + function OnProcessCrashed(subject, topic, data) { var id; subject = subject.QueryInterface(CI.nsIPropertyBag2); if (topic == "plugin-crashed") { id = subject.getPropertyAsAString("pluginDumpID"); } else if (topic == "ipc:content-shutdown") { id = subject.getPropertyAsAString("dumpID");
--- a/media/libvpx/Makefile.in +++ b/media/libvpx/Makefile.in @@ -405,17 +405,16 @@ ifdef VPX_NEED_OBJ_INT_EXTRACT # parse that format, and so only has limited support for cross-compilation. ifdef VPX_ARM_ASM VPX_OIE_FORMAT := rvds else VPX_OIE_FORMAT := gas endif -DISABLED_HOST_CSRCS = obj_int_extract.c HOST_PROGRAM = host_obj_int_extract$(HOST_BIN_SUFFIX) CSRCS += asm_com_offsets.c GARBAGE += asm_com_offsets.$(OBJ_SUFFIX) asm_com_offsets.asm ifdef MOZ_VP8_ENCODER CSRCS += asm_enc_offsets.c
--- a/mobile/android/chrome/content/aboutReader.js +++ b/mobile/android/chrome/content/aboutReader.js @@ -348,23 +348,56 @@ AboutReader.prototype = { bodyClasses.remove("font-size" + this._fontSize); this._fontSize = newFontSize; bodyClasses.add("font-size" + this._fontSize); Services.prefs.setIntPref("reader.font_size", this._fontSize); }, - _handleDeviceLight: function Reader_handleDeviceLight(luxValue) { + _handleDeviceLight: function Reader_handleDeviceLight(newLux) { + // Desired size of the this._luxValues array. + let luxValuesSize = 10; + // Add new lux value at the front of the array. + this._luxValues.unshift(newLux); + // Add new lux value to this._totalLux for averaging later. + this._totalLux += newLux; + + // Don't update when length of array is less than luxValuesSize except when it is 1. + if (this._luxValues.length < luxValuesSize) { + // Use the first lux value to set the color scheme until our array equals luxValuesSize. + if (this._luxValues.length == 1) { + this._updateColorScheme(newLux); + } + return; + } + // Holds the average of the lux values collected in this._luxValues. + let averageLuxValue = this._totalLux/luxValuesSize; + + this._updateColorScheme(averageLuxValue); + // Pop the oldest value off the array. + let oldLux = this._luxValues.pop(); + // Subtract oldLux since it has been discarded from the array. + this._totalLux -= oldLux; + }, + + _updateColorScheme: function Reader_updateColorScheme(luxValue) { + // Upper bound value for "dark" color scheme beyond which it changes to "light". + let upperBoundDark = 50; + // Lower bound value for "light" color scheme beyond which it changes to "dark". + let lowerBoundLight = 10; + // Threshold for color scheme change. + let colorChangeThreshold = 20; + // Ignore changes that are within a certain threshold of previous lux values. - if ((this._colorScheme === "dark" && luxValue < 50) || - (this._colorScheme === "light" && luxValue > 25)) + if ((this._colorScheme === "dark" && luxValue < upperBoundDark) || + (this._colorScheme === "light" && luxValue > lowerBoundLight)) return; - if (luxValue < 30) + if (luxValue < colorChangeThreshold) this._setColorScheme("dark"); else this._setColorScheme("light"); }, _setColorScheme: function Reader_setColorScheme(newColorScheme) { if (this._colorScheme === newColorScheme) return; @@ -378,19 +411,23 @@ AboutReader.prototype = { bodyClasses.add(this._colorScheme); }, // Pref values include "dark", "light", and "auto", which automatically switches // between light and dark color schemes based on the ambient light level. _setColorSchemePref: function Reader_setColorSchemePref(colorSchemePref) { if (colorSchemePref === "auto") { this._win.addEventListener("devicelight", this, false); + this._luxValues = []; + this._totalLux = 0; } else { this._win.removeEventListener("devicelight", this, false); this._setColorScheme(colorSchemePref); + delete this._luxValues; + delete this._totalLux; } Services.prefs.setCharPref("reader.color_scheme", colorSchemePref); }, _setFontType: function Reader_setFontType(newFontType) { if (this._fontType === newFontType) return;
--- a/mobile/android/components/FilePicker.js +++ b/mobile/android/components/FilePicker.js @@ -230,17 +230,17 @@ FilePicker.prototype = { mIndex: 0, hasMoreElements: function() { return (this.mIndex < this.mFiles.length); }, getNext: function() { if (this.mIndex >= this.mFiles.length) { throw Components.results.NS_ERROR_FAILURE; } - return map(this.mFiles[this.mIndex++]); + return mapFunction(this.mFiles[this.mIndex++]); } }; }, classID: Components.ID("{18a4e042-7c7c-424b-a583-354e68553a7f}"), QueryInterface: XPCOMUtils.generateQI([Ci.nsIFilePicker, Ci.nsIObserver]) };
new file mode 100755 --- /dev/null +++ b/mobile/android/debug_sign_tool.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python + +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +""" +Sign Android packages using an Android debug keystore, creating the +keystore if it does not exist. + +This and |zip| can be combined to replace the Android |apkbuilder| +tool, which was deprecated in SDK r22. + +Exits with code 0 if creating the keystore and every signing succeeded, +or with code 1 if any creation or signing failed. +""" + +from argparse import ArgumentParser +import errno +import logging +import os +import subprocess +import sys + + +log = logging.getLogger(os.path.basename(__file__)) +log.setLevel(logging.INFO) +sh = logging.StreamHandler(stream=sys.stdout) +sh.setFormatter(logging.Formatter('%(name)s: %(message)s')) +log.addHandler(sh) + + +class DebugKeystore: + """ + A thin abstraction on top of an Android debug key store. + """ + def __init__(self, keystore): + self._keystore = os.path.abspath(os.path.expanduser(keystore)) + self._alias = 'debug' + self.verbose = False + self.keytool = 'keytool' + self.jarsigner = 'jarsigner' + + @property + def keystore(self): + return self._keystore + + @property + def alias(self): + return self._alias + + def _ensure_keystore(self): + if os.path.exists(self.keystore): + if self.verbose: + log.debug('Keystore exists at %s' % self.keystore) + else: + self.create_keystore() + + def create_keystore(self): + try: + path = os.path.dirname(self.keystore) + os.makedirs(path) + except OSError as exception: + if exception.errno != errno.EEXIST: + raise + + args = [ self.keytool, + '-genkey', + '-v', + '-keystore', self.keystore, + '-storepass', 'android', + '-alias', self.alias, + '-keypass', 'android', + '-dname', 'CN=Android Debug,O=Android,C=US', + '-keyalg', 'RSA', + '-validity', '365', + ] + subprocess.check_call(args) + if self.verbose: + log.info('Created keystore at %s' % self.keystore) + + def sign(self, apk): + self._ensure_keystore() + + args = [ self.jarsigner, + '-digestalg', 'SHA1', + '-sigalg', 'MD5withRSA', + '-keystore', self.keystore, + '-storepass', 'android', + apk, + self.alias, + ] + subprocess.check_call(args) + if self.verbose: + log.info('Signed %s with keystore at %s' % (apk, self.keystore)) + + +def parse_args(argv): + parser = ArgumentParser(description='Sign Android packages using an Android debug keystore.') + parser.add_argument('apks', nargs='+', + metavar='APK', + help='Android packages to be signed') + parser.add_argument('-q', '--quiet', + dest='verbose', + default=True, + action='store_false', + help='quiet output') + parser.add_argument('--keytool', + metavar='PATH', + default='keytool', + help='path to Java keytool') + parser.add_argument('--jarsigner', + metavar='PATH', + default='jarsigner', + help='path to Java jarsigner') + parser.add_argument('--keystore', + metavar='PATH', + default='~/.android/debug.keystore', + help='path to keystore (default: ~/.android/debug.keystore)') + parser.add_argument('-f', '--force-create-keystore', + dest='force', + default=False, + action='store_true', + help='force creating keystore') + return parser.parse_args(argv) + + +def main(): + args = parse_args(sys.argv[1:]) + + keystore = DebugKeystore(args.keystore) + keystore.verbose = args.verbose + keystore.keytool = args.keytool + keystore.jarsigner = args.jarsigner + + if args.force: + try: + keystore.create_keystore() + except subprocess.CalledProcessError as e: + log.error('Failed to force-create keystore') + log.error(e) + return 1 + + for apk in args.apks: + try: + keystore.sign(apk) + except subprocess.CalledProcessError as e: + log.error('Failed to sign %s', apk) + log.error(e) + return 1 + + return 0 + +if __name__ == '__main__': + sys.exit(main())
--- a/modules/libbz2/src/Makefile.in +++ b/modules/libbz2/src/Makefile.in @@ -29,16 +29,15 @@ CSRCS = \ bzlib.c \ compress.c \ crctable.c \ decompress.c \ huffman.c \ randtable.c \ $(NULL) -DISABLED_HOST_CSRCS = $(CSRCS) include $(topsrcdir)/config/rules.mk # The intermediate (.ii/.s) files for host and target can have the same name... # disable parallel builds .NOTPARALLEL:
--- a/modules/libmar/src/Makefile.in +++ b/modules/libmar/src/Makefile.in @@ -15,24 +15,19 @@ LIBRARY_NAME = mar DISABLED_HOST_LIBRARY_NAME = hostmar FORCE_STATIC_LIB = 1 ifeq ($(OS_ARCH),WINNT) USE_STATIC_LIBS = 1 endif # This makefile just builds support for reading archives. - -DISABLED_HOST_CSRCS = \ - mar_create.c \ - mar_extract.c \ - mar_read.c \ - $(NULL) - -CSRCS = \ - $(HOST_CSRCS) \ - $(NULL) +CSRCS = \ + mar_create.c \ + mar_extract.c \ + mar_read.c \ + $(NULL) include $(topsrcdir)/config/rules.mk # The intermediate (.ii/.s) files for host and target can have the same name... # disable parallel builds .NOTPARALLEL:
--- a/modules/libmar/tool/Makefile.in +++ b/modules/libmar/tool/Makefile.in @@ -35,19 +35,16 @@ DEFINES += \ $(NULL) endif HOST_CFLAGS += \ -DNO_SIGN_VERIFY \ $(DEFINES) \ $(NULL) -DISABLED_HOST_CSRCS = \ - mar.c \ - $(NULL) DISABLED_CSRCS = $(HOST_CSRCS) HOST_LIBS = $(DIST)/host/lib/$(LIB_PREFIX)hostmar.$(LIB_SUFFIX) LIBS = $(DEPTH)/modules/libmar/src/$(LIB_PREFIX)mar.$(LIB_SUFFIX) ifdef MOZ_ENABLE_SIGNMAR LIBS += \ $(DEPTH)/modules/libmar/sign/$(LIB_PREFIX)signmar.$(LIB_SUFFIX) \
--- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -1799,17 +1799,16 @@ pref("layout.frame_rate", -1); // heavily loaded. pref("layout.frame_rate.precise", false); // pref to permit users to make verified SOAP calls by default pref("capability.policy.default.SOAPCall.invokeVerifySourceHeader", "allAccess"); // if true, allow plug-ins to override internal imglib decoder mime types in full-page mode pref("plugin.override_internal_types", false); -pref("plugin.expose_full_path", false); // if true navigator.plugins reveals full path // See bug 136985. Gives embedders a pref to hook into to show // a popup blocker if they choose. pref("browser.popups.showPopupBlocker", true); // Pref to control whether the viewmanager code does double-buffering or not // See http://bugzilla.mozilla.org/show_bug.cgi?id=169483 for further details... pref("viewmanager.do_doublebuffering", true);
--- a/netwerk/base/public/nsIBrowserSearchService.idl +++ b/netwerk/base/public/nsIBrowserSearchService.idl @@ -135,16 +135,40 @@ interface nsISearchEngine : nsISupports /** * An optional unique identifier for this search engine within the context of * the distribution, as provided by the distributing entity. */ readonly attribute AString identifier; }; +[scriptable, uuid(9fc39136-f08b-46d3-b232-96f4b7b0e235)] +interface nsISearchInstallCallback : nsISupports +{ + const unsigned long ERROR_UNKNOWN_FAILURE = 0x1; + const unsigned long ERROR_DUPLICATE_ENGINE = 0x2; + + /** + * Called to indicate that the engine addition process succeeded. + * + * @param engine + * The nsISearchEngine object that was added (will not be null). + */ + void onSuccess(in nsISearchEngine engine); + + /** + * Called to indicate that the engine addition process failed. + * + * @param errorCode + * One of the ERROR_* values described above indicating the cause of + * the failure. + */ + void onError(in unsigned long errorCode); +}; + /** * Callback for asynchronous initialization of nsIBrowserSearchService */ [scriptable, function, uuid(02256156-16e4-47f1-9979-76ff98ceb590)] interface nsIBrowserSearchInitObserver : nsISupports { /** * Called once initialization of the browser search service is complete. @@ -181,18 +205,17 @@ interface nsIBrowserSearchService : nsIS * initialization has not been triggered yet. */ readonly attribute bool isInitialized; /** * Adds a new search engine from the file at the supplied URI, optionally * asking the user for confirmation first. If a confirmation dialog is * shown, it will offer the option to begin using the newly added engine - * right away; if no confirmation dialog is shown, the new engine will be - * used right away automatically. + * right away. * * @param engineURL * The URL to the search engine's description file. * * @param dataType * An integer representing the plugin file format. Must be one * of the supported search engine data types defined above. * @@ -202,21 +225,26 @@ interface nsIBrowserSearchService : nsIS * engine description file. * * @param confirm * A boolean value indicating whether the user should be asked for * confirmation before this engine is added to the list. If this * value is false, the engine will be added to the list upon successful * load, but it will not be selected as the current engine. * + * @param callback + * A nsISearchInstallCallback that will be notified when the + * addition is complete, or if the addition fails. It will not be + * called if addEngine throws an exception. + * * @throws NS_ERROR_FAILURE if the type is invalid, or if the description * file cannot be successfully loaded. */ void addEngine(in AString engineURL, in long dataType, in AString iconURL, - in boolean confirm); + in boolean confirm, [optional] in nsISearchInstallCallback callback); /** * Adds a new search engine, without asking the user for confirmation and * without starting to use it right away. * * @param name * The search engine's name. Must be unique. Must not be null. *
--- a/netwerk/base/src/nsSocketTransport2.cpp +++ b/netwerk/base/src/nsSocketTransport2.cpp @@ -405,41 +405,42 @@ nsSocketInputStream::CloseWithStatus(nsr NS_IMETHODIMP nsSocketInputStream::AsyncWait(nsIInputStreamCallback *callback, uint32_t flags, uint32_t amount, nsIEventTarget *target) { SOCKET_LOG(("nsSocketInputStream::AsyncWait [this=%p]\n", this)); - // This variable will be non-null when we want to call the callback - // directly from this function, but outside the lock. - // (different from callback when target is not null) - nsCOMPtr<nsIInputStreamCallback> directCallback; + bool hasError = false; { MutexAutoLock lock(mTransport->mLock); if (callback && target) { // // build event proxy // mCallback = NS_NewInputStreamReadyEvent(callback, target); } else mCallback = callback; + mCallbackFlags = flags; - if (NS_FAILED(mCondition)) - directCallback.swap(mCallback); - else - mCallbackFlags = flags; + hasError = NS_FAILED(mCondition); + } // unlock mTransport->mLock + + if (hasError) { + // OnSocketEvent will call OnInputStreamReady with an error code after + // going through the event loop. We do this because most socket callers + // do not expect AsyncWait() to synchronously execute the OnInputStreamReady + // callback. + mTransport->PostEvent(nsSocketTransport::MSG_INPUT_PENDING); + } else { + mTransport->OnInputPending(); } - if (directCallback) - directCallback->OnInputStreamReady(this); - else - mTransport->OnInputPending(); return NS_OK; } //----------------------------------------------------------------------------- // socket output stream impl //-----------------------------------------------------------------------------
--- a/other-licenses/bsdiff/Makefile.in +++ b/other-licenses/bsdiff/Makefile.in @@ -27,17 +27,16 @@ topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk # This program is output to dist/host/bin because it is only needed by the # build system and is not intended to be included in Mozilla distributions. HOST_PROGRAM = mbsdiff$(BIN_SUFFIX) -DISABLED_HOST_CSRCS = bsdiff.c ifdef MOZ_NATIVE_BZ2 HOST_LIBS += $(MOZ_BZ2_LIBS) else HOST_LIBS += $(DIST)/host/lib/$(LIB_PREFIX)hostbz2.$(LIB_SUFFIX) endif ifeq ($(HOST_OS_ARCH),WINNT)
--- a/parser/html/nsHtml5TreeOperation.cpp +++ b/parser/html/nsHtml5TreeOperation.cpp @@ -27,16 +27,17 @@ #include "nsIObserverService.h" #include "mozilla/Services.h" #include "nsIMutationObserver.h" #include "nsIFormProcessor.h" #include "nsIServiceManager.h" #include "nsEscape.h" #include "mozilla/dom/Comment.h" #include "mozilla/dom/Element.h" +#include "mozilla/dom/HTMLImageElement.h" #include "mozilla/dom/HTMLTemplateElement.h" #include "nsHtml5SVGLoadDispatcher.h" #include "nsIURI.h" #include "nsIProtocolHandler.h" #include "nsNetUtil.h" #include "nsIHTMLDocument.h" #include "mozilla/Likely.h" #include "nsTextNode.h" @@ -436,25 +437,32 @@ nsHtml5TreeOperation::Perform(nsHtml5Tre } return rv; } case eTreeOpSetFormElement: { nsIContent* node = *(mOne.node); nsIContent* parent = *(mTwo.node); nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(node)); + nsCOMPtr<nsIDOMHTMLImageElement> domImageElement = do_QueryInterface(node); // NS_ASSERTION(formControl, "Form-associated element did not implement nsIFormControl."); // TODO: uncomment the above line when <keygen> (bug 101019) is supported by Gecko nsCOMPtr<nsIDOMHTMLFormElement> formElement(do_QueryInterface(parent)); NS_ASSERTION(formElement, "The form element doesn't implement nsIDOMHTMLFormElement."); // avoid crashing on <keygen> if (formControl && !node->HasAttr(kNameSpaceID_None, nsGkAtoms::form)) { formControl->SetForm(formElement); + } else if (domImageElement) { + nsRefPtr<dom::HTMLImageElement> imageElement = + static_cast<dom::HTMLImageElement*>(domImageElement.get()); + MOZ_ASSERT(imageElement); + imageElement->SetForm(formElement); } + return rv; } case eTreeOpAppendText: { nsIContent* parent = *mOne.node; PRUnichar* buffer = mTwo.unicharPtr; uint32_t length = mFour.integer; return AppendText(buffer, length, parent, aBuilder); }
--- a/python/mozbuild/mozbuild/frontend/emitter.py +++ b/python/mozbuild/mozbuild/frontend/emitter.py @@ -88,16 +88,17 @@ class TreeMetadataEmitter(object): EXTRA_JS_MODULES='EXTRA_JS_MODULES', EXTRA_PP_COMPONENTS='EXTRA_PP_COMPONENTS', HOST_CSRCS='HOST_CSRCS', HOST_LIBRARY_NAME='HOST_LIBRARY_NAME', JS_MODULES_PATH='JS_MODULES_PATH', LIBRARY_NAME='LIBRARY_NAME', LIBS='LIBS', MODULE='MODULE', + SDK_LIBRARY='SDK_LIBRARY', SIMPLE_PROGRAMS='SIMPLE_PROGRAMS', SSRCS='SSRCS', XPIDL_FLAGS='XPIDL_FLAGS', XPIDL_MODULE='XPIDL_MODULE', XPIDLSRCS='XPIDL_SOURCES', ) for mak, moz in varmap.items(): if sandbox[moz]:
--- a/python/mozbuild/mozbuild/frontend/sandbox_symbols.py +++ b/python/mozbuild/mozbuild/frontend/sandbox_symbols.py @@ -155,16 +155,22 @@ VARIABLES = { """), 'LIBS': (StrictOrderingOnAppendList, list, [], """Linker libraries and flags. A list of libraries and flags to include when linking. """), + 'SDK_LIBRARY': (StrictOrderingOnAppendList, list, [], + """Elements of the distributed SDK. + + Files on this list will be copied into SDK_LIB_DIR ($DIST/sdk/lib). + """), + 'SIMPLE_PROGRAMS': (StrictOrderingOnAppendList, list, [], """Generate a list of binaries from source. A list of sources, one per program, to compile & link with libs into standalone programs. """), 'SSRCS': (StrictOrderingOnAppendList, list, [], """Assembly source files.
--- a/python/mozbuild/mozbuild/test/backend/data/variable_passthru/moz.build +++ b/python/mozbuild/mozbuild/test/backend/data/variable_passthru/moz.build @@ -15,15 +15,17 @@ EXTRA_PP_COMPONENTS = ['bar.pp.js', 'foo HOST_CSRCS = ['bar.c', 'foo.c'] HOST_LIBRARY_NAME = 'host_bar' LIBRARY_NAME = 'lib_name' LIBS = ['bar.lib', 'foo.lib'] +SDK_LIBRARY = ['bar.sdk', 'foo.sdk'] + SIMPLE_PROGRAMS = ['bar.x', 'foo.x'] CSRCS += ['bar.c', 'foo.c'] CMMSRCS = ['bar.mm', 'foo.mm'] SSRCS = ['bar.S', 'foo.S']
--- a/python/mozbuild/mozbuild/test/backend/test_recursivemake.py +++ b/python/mozbuild/mozbuild/test/backend/test_recursivemake.py @@ -167,16 +167,20 @@ class TestRecursiveMakeBackend(BackendTe 'HOST_CSRCS += foo.c', ], 'HOST_LIBRARY_NAME': [ 'HOST_LIBRARY_NAME := host_bar', ], 'LIBRARY_NAME': [ 'LIBRARY_NAME := lib_name', ], + 'SDK_LIBRARY': [ + 'SDK_LIBRARY += bar.sdk', + 'SDK_LIBRARY += foo.sdk', + ], 'SIMPLE_PROGRAMS': [ 'SIMPLE_PROGRAMS += bar.x', 'SIMPLE_PROGRAMS += foo.x', ], 'SSRCS': [ 'SSRCS += bar.S', 'SSRCS += foo.S', ],
--- a/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/moz.build +++ b/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/moz.build @@ -15,15 +15,17 @@ EXTRA_PP_COMPONENTS=['fans.pp.js', 'tans HOST_CSRCS += ['fans.c', 'tans.c'] HOST_LIBRARY_NAME = 'host_fans' LIBRARY_NAME = 'lib_name' LIBS += ['fans.lib', 'tans.lib'] +SDK_LIBRARY += ['fans.sdk', 'tans.sdk'] + SIMPLE_PROGRAMS += ['fans.x', 'tans.x'] CSRCS += ['fans.c', 'tans.c'] CMMSRCS = ['fans.mm', 'tans.mm'] SSRCS = ['fans.S', 'tans.S']
--- a/python/mozbuild/mozbuild/test/frontend/test_emitter.py +++ b/python/mozbuild/mozbuild/test/frontend/test_emitter.py @@ -130,16 +130,17 @@ class TestEmitterBasic(unittest.TestCase CSRCS=['fans.c', 'tans.c'], DEFINES=['-Dfans', '-Dtans'], EXTRA_COMPONENTS=['fans.js', 'tans.js'], EXTRA_PP_COMPONENTS=['fans.pp.js', 'tans.pp.js'], HOST_CSRCS=['fans.c', 'tans.c'], HOST_LIBRARY_NAME='host_fans', LIBRARY_NAME='lib_name', LIBS=['fans.lib', 'tans.lib'], + SDK_LIBRARY=['fans.sdk', 'tans.sdk'], SIMPLE_PROGRAMS=['fans.x', 'tans.x'], SSRCS=['fans.S', 'tans.S'], XPIDLSRCS=['bar.idl', 'biz.idl', 'foo.idl'], XPIDL_MODULE='module_name', XPIDL_FLAGS=['-Idir1', '-Idir2', '-Idir3'], ) variables = objs[1].variables
--- a/testing/talos/talos.json +++ b/testing/talos/talos.json @@ -1,6 +1,6 @@ { "talos.zip": { - "url": "http://build.mozilla.org/talos/zips/talos.dfe08f329e2c.zip", + "url": "http://build.mozilla.org/talos/zips/talos.8cf5b862d113.zip", "path": "" } }
--- a/testing/testsuite-targets.mk +++ b/testing/testsuite-targets.mk @@ -61,17 +61,17 @@ RUN_MOCHITEST_REMOTE = \ --console-level=INFO --log-file=./$@.log --file-level=INFO $(DM_FLAGS) --dm_trans=$(DM_TRANS) \ --app=$(TEST_PACKAGE_NAME) --deviceIP=${TEST_DEVICE} --xre-path=${MOZ_HOST_BIN} \ --testing-modules-dir=$(call core_abspath,_tests/modules) \ $(SYMBOLS_PATH) $(TEST_PATH_ARG) $(EXTRA_TEST_ARGS) RUN_MOCHITEST_ROBOCOP = \ rm -f ./$@.log && \ $(PYTHON) _tests/testing/mochitest/runtestsremote.py \ - --robocop-apk=$(DEPTH)/build/mobile/robocop/robocop-debug-signed.apk \ + --robocop-apk=$(DEPTH)/build/mobile/robocop/robocop-debug.apk \ --robocop-ids=$(DEPTH)/mobile/android/base/fennec_ids.txt \ --robocop-ini=$(DEPTH)/build/mobile/robocop/robocop.ini \ --console-level=INFO --log-file=./$@.log --file-level=INFO $(DM_FLAGS) --dm_trans=$(DM_TRANS) \ --app=$(TEST_PACKAGE_NAME) --deviceIP=${TEST_DEVICE} --xre-path=${MOZ_HOST_BIN} \ $(SYMBOLS_PATH) $(TEST_PATH_ARG) $(EXTRA_TEST_ARGS) ifndef NO_FAIL_ON_TEST_ERRORS define check_test_error_internal
--- a/toolkit/components/search/nsSearchService.js +++ b/toolkit/components/search/nsSearchService.js @@ -1,15 +1,16 @@ # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. const Ci = Components.interfaces; const Cc = Components.classes; const Cr = Components.results; +const Cu = Components.utils; Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); Components.utils.import("resource://gre/modules/Services.jsm"); Components.utils.import("resource://gre/modules/commonjs/sdk/core/promise.js"); XPCOMUtils.defineLazyModuleGetter(this, "DeferredTask", "resource://gre/modules/DeferredTask.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "OS", @@ -1145,17 +1146,20 @@ Engine.prototype = { set _uri(aValue) { this.__uri = aValue; }, // Whether to obtain user confirmation before adding the engine. This is only // used when the engine is first added to the list. _confirm: false, // Whether to set this as the current engine as soon as it is loaded. This // is only used when the engine is first added to the list. - _useNow: true, + _useNow: false, + // A function to be invoked when this engine object's addition completes (or + // fails). Only used for installation via addEngine. + _installCallback: null, // Where the engine was loaded from. Can be one of: SEARCH_APP_DIR, // SEARCH_PROFILE_DIR, SEARCH_IN_EXTENSION. __installLocation: null, // The number of days between update checks for new versions _updateInterval: null, // The url to check at for a new update _updateURL: null, // The url to check for a new icon @@ -1299,41 +1303,50 @@ Engine.prototype = { /** * Handle the successful download of an engine. Initializes the engine and * triggers parsing of the data. The engine is then flushed to disk. Notifies * the search service once initialization is complete. */ _onLoad: function SRCH_ENG_onLoad(aBytes, aEngine) { /** - * Handle an error during the load of an engine by prompting the user to - * notify him that the load failed. + * Handle an error during the load of an engine by notifying the engine's + * error callback, if any. */ - function onError(aErrorString, aTitleString) { + function onError(errorCode = Ci.nsISearchInstallCallback.ERROR_UNKNOWN_FAILURE) { + // Notify the callback of the failure + if (aEngine._installCallback) { + aEngine._installCallback(errorCode); + } + } + + function promptError(strings = {}, error = undefined) { + onError(error); + if (aEngine._engineToUpdate) { // We're in an update, so just fail quietly LOG("updating " + aEngine._engineToUpdate.name + " failed"); return; } var brandBundle = Services.strings.createBundle(BRAND_BUNDLE); var brandName = brandBundle.GetStringFromName("brandShortName"); var searchBundle = Services.strings.createBundle(SEARCH_BUNDLE); - var msgStringName = aErrorString || "error_loading_engine_msg2"; - var titleStringName = aTitleString || "error_loading_engine_title"; + var msgStringName = strings.error || "error_loading_engine_msg2"; + var titleStringName = strings.title || "error_loading_engine_title"; var title = searchBundle.GetStringFromName(titleStringName); var text = searchBundle.formatStringFromName(msgStringName, [brandName, aEngine._location], 2); Services.ww.getNewPrompter(null).alert(title, text); } if (!aBytes) { - onError(); + promptError(); return; } var engineToUpdate = null; if (aEngine._engineToUpdate) { engineToUpdate = aEngine._engineToUpdate.wrappedJSObject; // Make this new engine use the old engine's file. @@ -1346,53 +1359,55 @@ Engine.prototype = { createInstance(Ci.nsIDOMParser); var doc = parser.parseFromBuffer(aBytes, aBytes.length, "text/xml"); aEngine._data = doc.documentElement; break; case SEARCH_DATA_TEXT: aEngine._data = aBytes; break; default: - onError(); + promptError(); LOG("_onLoad: Bogus engine _dataType: \"" + this._dataType + "\""); return; } try { // Initialize the engine from the obtained data aEngine._initFromData(); } catch (ex) { LOG("_onLoad: Failed to init engine!\n" + ex); // Report an error to the user - onError(); + promptError(); return; } // Check to see if this is a duplicate engine. If we're confirming the // engine load, then we display a "this is a duplicate engine" prompt, // otherwise we fail silently. if (!engineToUpdate) { if (Services.search.getEngineByName(aEngine.name)) { - if (aEngine._confirm) - onError("error_duplicate_engine_msg", "error_invalid_engine_title"); - + promptError({ error: "error_duplicate_engine_msg", + title: "error_invalid_engine_title" + }, Ci.nsISearchInstallCallback.ERROR_DUPLICATE_ENGINE); LOG("_onLoad: duplicate engine found, bailing"); return; } } // If requested, confirm the addition now that we have the title. // This property is only ever true for engines added via // nsIBrowserSearchService::addEngine. if (aEngine._confirm) { var confirmation = aEngine._confirmAddEngine(); LOG("_onLoad: confirm is " + confirmation.confirmed + "; useNow is " + confirmation.useNow); - if (!confirmation.confirmed) + if (!confirmation.confirmed) { + onError(); return; + } aEngine._useNow = confirmation.useNow; } // If we don't yet have a file, get one now. The only case where we would // already have a file is if this is an update and _file was set above. if (!aEngine._file) aEngine._file = getSanitizedFile(aEngine.name); @@ -1409,44 +1424,47 @@ Engine.prototype = { let newUpdateURL = aEngine._updateURL; let oldSelfURL = engineToUpdate._getURLOfType(URLTYPE_OPENSEARCH); if (oldSelfURL && oldSelfURL._hasRelation("self")) { oldUpdateURL = oldSelfURL.template; let newSelfURL = aEngine._getURLOfType(URLTYPE_OPENSEARCH); if (!newSelfURL || !newSelfURL._hasRelation("self")) { LOG("_onLoad: updateURL missing in updated engine for " + aEngine.name + " aborted"); + onError(); return; } newUpdateURL = newSelfURL.template; } if (oldUpdateURL != newUpdateURL) { LOG("_onLoad: updateURLs do not match! Update of " + aEngine.name + " aborted"); + onError(); return; } } // Set the new engine's icon, if it doesn't yet have one. if (!aEngine._iconURI && engineToUpdate._iconURI) aEngine._iconURI = engineToUpdate._iconURI; - - // Clear the "use now" flag since we don't want to be changing the - // current engine for an update. - aEngine._useNow = false; } // Write the engine to file. For readOnly engines, they'll be stored in the // cache following the notification below. if (!aEngine._readOnly) aEngine._serializeToFile(); // Notify the search service of the successful load. It will deal with // updates by checking aEngine._engineToUpdate. notifyAction(aEngine, SEARCH_ENGINE_LOADED); + + // Notify the callback if needed + if (aEngine._installCallback) { + aEngine._installCallback(); + } }, /** * Sets the .iconURI property of the engine. * * @param aIconURL * A URI string pointing to the engine's icon. Must have a http[s], * ftp, or data scheme. Icons with HTTP[S] or FTP schemes will be @@ -3336,24 +3354,41 @@ SearchService.prototype = { var engine = new Engine(getSanitizedFile(aName), SEARCH_DATA_XML, false); engine._initFromMetadata(aName, aIconURL, aAlias, aDescription, aMethod, aTemplate); this._addEngineToStore(engine); this._batchCacheInvalidation(); }, addEngine: function SRCH_SVC_addEngine(aEngineURL, aDataType, aIconURL, - aConfirm) { + aConfirm, aCallback) { LOG("addEngine: Adding \"" + aEngineURL + "\"."); this._ensureInitialized(); try { var uri = makeURI(aEngineURL); var engine = new Engine(uri, aDataType, false); + if (aCallback) { + engine._installCallback = function (errorCode) { + try { + if (errorCode == null) + aCallback.onSuccess(engine); + else + aCallback.onError(errorCode); + } catch (ex) { + Cu.reportError("Error invoking addEngine install callback: " + ex); + } + // Clear the reference to the callback now that it's been invoked. + engine._installCallback = null; + }; + } engine._initFromURI(); } catch (ex) { + // Drop the reference to the callback, if set + if (engine) + engine._installCallback = null; FAIL("addEngine: Error adding engine:\n" + ex, Cr.NS_ERROR_FAILURE); } engine._setIcon(aIconURL, false); engine._confirm = aConfirm; }, removeEngine: function SRCH_SVC_removeEngine(aEngine) { this._ensureInitialized();
--- a/toolkit/components/search/tests/xpcshell/head_search.js +++ b/toolkit/components/search/tests/xpcshell/head_search.js @@ -92,17 +92,17 @@ function afterCache(callback) callback(result); } else { dump("TOPIC: " + topic+ "\n"); } } Services.obs.addObserver(obs, "browser-search-service", false); } -function parseJsonFromStream(aInputStream) { +function parseJsonFromStream(aInputStream) { const json = Cc["@mozilla.org/dom/json;1"].createInstance(Components.interfaces.nsIJSON); const data = json.decodeFromStream(aInputStream, aInputStream.available()); return data; } /** * Read a JSON file and return the JS object */
new file mode 100644 --- /dev/null +++ b/toolkit/components/search/tests/xpcshell/test_addEngine_callback.js @@ -0,0 +1,123 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests covering nsIBrowserSearchService::addEngine's optional callback. + */ + +"use strict"; + +const Ci = Components.interfaces; + +Components.utils.import("resource://testing-common/httpd.js"); + +// Override the prompt service and nsIPrompt, since the search service currently +// prompts in response to certain installation failures we test here +// XXX this should disappear once bug 863474 is fixed +function replaceService(contractID, component) { + let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); + let cid = registrar.contractIDToCID(contractID); + + let oldFactory = Components.manager.getClassObject(Components.classes[contractID], + Ci.nsIFactory); + registrar.unregisterFactory(cid, oldFactory); + + let factory = { + createInstance: function(aOuter, aIid) { + if (aOuter != null) + throw Components.results.NS_ERROR_NO_AGGREGATION; + return component.QueryInterface(aIid); + } + }; + + registrar.registerFactory(cid, "", contractID, factory); +} +// Only need to stub the methods actually called by nsSearchService +let promptService = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPromptService]), + confirmEx: function() {} +}; +let prompt = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrompt]), + alert: function() {} +}; +replaceService("@mozilla.org/embedcomp/prompt-service;1", promptService); +replaceService("@mozilla.org/prompter;1", prompt); + + +// First test inits the search service +add_test(function init_search_service() { + Services.search.init(function (status) { + if (!Components.isSuccessCode(status)) + do_throw("Failed to initialize search service"); + + run_next_test(); + }); +}); + +// Simple test of the search callback +add_test(function simple_callback_test() { + let searchCallback = { + onSuccess: function (engine) { + do_check_true(!!engine); + do_check_neq(engine.name, Services.search.defaultEngine.name); + run_next_test(); + }, + onError: function (errorCode) { + do_throw("search callback returned error: " + errorCode); + } + } + Services.search.addEngine("http://localhost:4444/data/engine.xml", + Ci.nsISearchEngine.DATA_XML, + null, false, searchCallback); +}); + +// Test of the search callback on duplicate engine failures +add_test(function duplicate_failure_test() { + let searchCallback = { + onSuccess: function (engine) { + do_throw("this addition should not have succeeded"); + }, + onError: function (errorCode) { + do_check_true(!!errorCode); + do_check_eq(errorCode, Ci.nsISearchInstallCallback.ERROR_DUPLICATE_ENGINE); + run_next_test(); + } + } + // Re-add the same engine added in the previous test + Services.search.addEngine("http://localhost:4444/data/engine.xml", + Ci.nsISearchEngine.DATA_XML, + null, false, searchCallback); +}); + +// Test of the search callback on failure to load the engine failures +add_test(function load_failure_test() { + let searchCallback = { + onSuccess: function (engine) { + do_throw("this addition should not have succeeded"); + }, + onError: function (errorCode) { + do_check_true(!!errorCode); + do_check_eq(errorCode, Ci.nsISearchInstallCallback.ERROR_UNKNOWN_FAILURE); + run_next_test(); + } + } + // Try adding an engine that doesn't exist + Services.search.addEngine("http://invalid/data/engine.xml", + Ci.nsISearchEngine.DATA_XML, + null, false, searchCallback); +}); + +function run_test() { + updateAppInfo(); + + let httpServer = new HttpServer(); + httpServer.start(4444); + httpServer.registerDirectory("/", do_get_cwd()); + + do_register_cleanup(function cleanup() { + httpServer.stop(function() {}); + }); + + run_next_test(); +}
--- a/toolkit/components/search/tests/xpcshell/test_notifications.js +++ b/toolkit/components/search/tests/xpcshell/test_notifications.js @@ -35,18 +35,17 @@ function search_observer(subject, topic, do_print("Observer: " + data + " for " + engine.name); switch (data) { case "engine-added": let retrievedEngine = Services.search.getEngineByName("Test search engine"); do_check_eq(engine, retrievedEngine); Services.search.defaultEngine = engine; - // XXX bug 493051 - // Services.search.currentEngine = engine; + Services.search.currentEngine = engine; do_execute_soon(function () { Services.search.removeEngine(engine); }); break; case "engine-removed": let engineNameOutput = " for Test search engine"; expectedLog = expectedLog.map(logLine => logLine + engineNameOutput); do_print("expectedLog:\n" + expectedLog.join("\n"))
--- a/toolkit/components/search/tests/xpcshell/xpcshell.ini +++ b/toolkit/components/search/tests/xpcshell/xpcshell.ini @@ -14,9 +14,9 @@ skip-if = debug && os == "linux" [test_migratedb.js] [test_nodb.js] [test_nodb_pluschanges.js] [test_save_sorted_engines.js] [test_purpose.js] [test_defaultEngine.js] [test_prefSync.js] [test_notifications.js] - +[test_addEngine_callback.js]
--- a/toolkit/components/social/FrameWorker.jsm +++ b/toolkit/components/social/FrameWorker.jsm @@ -25,26 +25,26 @@ XPCOMUtils.defineLazyModuleGetter(this, this.EXPORTED_SYMBOLS = ["getFrameWorkerHandle"]; var workerCache = {}; // keyed by URL. var _nextPortId = 1; // Retrieves a reference to a WorkerHandle associated with a FrameWorker and a // new ClientPort. this.getFrameWorkerHandle = - function getFrameWorkerHandle(url, clientWindow, name, origin) { + function getFrameWorkerHandle(url, clientWindow, name, origin, exposeLocalStorage = false) { // first create the client port we are going to use. Later we will // message the worker to create the worker port. let portid = _nextPortId++; let clientPort = new ClientPort(portid, clientWindow); let existingWorker = workerCache[url]; if (!existingWorker) { // setup the worker and add this connection to the pending queue - let worker = new FrameWorker(url, name, origin); + let worker = new FrameWorker(url, name, origin, exposeLocalStorage); worker.pendingPorts.push(clientPort); existingWorker = workerCache[url] = worker; } else { // already have a worker - either queue or make the connection. if (existingWorker.loaded) { try { clientPort._createWorkerAndEntangle(existingWorker); } @@ -64,25 +64,26 @@ this.getFrameWorkerHandle = * FrameWorker * * A FrameWorker is an iframe that is attached to the hiddenWindow, * which contains a pair of MessagePorts. It is constructed with the * URL of some JavaScript that will be run in the context of the window; * the script does not have a full DOM but is instead run in a sandbox * that has a select set of methods cloned from the URL's domain. */ -function FrameWorker(url, name, origin) { +function FrameWorker(url, name, origin, exposeLocalStorage) { this.url = url; this.name = name || url; this.ports = new Map(); this.pendingPorts = []; this.loaded = false; this.reloading = false; this.origin = origin; this._injectController = null; + this.exposeLocalStorage = exposeLocalStorage; this.frame = makeHiddenFrame(); this.load(); } FrameWorker.prototype = { load: function FrameWorker_loadWorker() { this._injectController = function(doc, topic, data) { @@ -128,21 +129,27 @@ FrameWorker.prototype = { createSandbox: function createSandbox() { let workerWindow = this.frame.contentWindow; let sandbox = new Cu.Sandbox(workerWindow); // copy the window apis onto the sandbox namespace only functions or // objects that are naturally a part of an iframe, I'm assuming they are // safe to import this way - let workerAPI = ['WebSocket', 'localStorage', 'atob', 'btoa', + let workerAPI = ['WebSocket', 'atob', 'btoa', 'clearInterval', 'clearTimeout', 'dump', 'setInterval', 'setTimeout', 'XMLHttpRequest', 'FileReader', 'Blob', 'EventSource', 'indexedDB', 'location']; + + // Only expose localStorage if the caller opted-in + if (this.exposeLocalStorage) { + workerAPI.push('localStorage'); + } + // Bug 798660 - XHR and WebSocket have issues in a sandbox and need // to be unwrapped to work let needsWaive = ['XMLHttpRequest', 'WebSocket']; // Methods need to be bound with the proper |this|. let needsBind = ['atob', 'btoa', 'dump', 'setInterval', 'clearInterval', 'setTimeout', 'clearTimeout']; workerAPI.forEach(function(fn) { try {
--- a/toolkit/components/social/SocialService.jsm +++ b/toolkit/components/social/SocialService.jsm @@ -132,16 +132,52 @@ let SocialServiceInternal = { } }); } finally { stmt.finalize(); } } }; +XPCOMUtils.defineLazyGetter(SocialServiceInternal, "providers", function () { + initService(); + let providers = {}; + for (let manifest of this.manifests) { + try { + if (ActiveProviders.has(manifest.origin)) { + let activationType = getOriginActivationType(manifest.origin); + let blessed = activationType == "builtin" || + activationType == "whitelist"; + let provider = new SocialProvider(manifest, blessed); + providers[provider.origin] = provider; + } + } catch (err) { + Cu.reportError("SocialService: failed to load provider: " + manifest.origin + + ", exception: " + err); + } + } + return providers; +}); + +function getOriginActivationType(origin) { + let prefname = SocialServiceInternal.getManifestPrefname(origin); + if (Services.prefs.getDefaultBranch("social.manifest.").getPrefType(prefname) == Services.prefs.PREF_STRING) + return 'builtin'; + + let whitelist = Services.prefs.getCharPref("social.whitelist").split(','); + if (whitelist.indexOf(origin) >= 0) + return 'whitelist'; + + let directories = Services.prefs.getCharPref("social.directories").split(','); + if (directories.indexOf(origin) >= 0) + return 'directory'; + + return 'foreign'; +} + let ActiveProviders = { get _providers() { delete this._providers; this._providers = {}; try { let pref = Services.prefs.getComplexValue("social.activeProviders", Ci.nsISupportsString); this._providers = JSON.parse(pref); @@ -299,33 +335,16 @@ function initService() { // enabled providers are not migrated. Cu.reportError("Error migrating social settings: " + e); } // Initialize the MozSocialAPI if (SocialServiceInternal.enabled) MozSocialAPI.enabled = true; } -XPCOMUtils.defineLazyGetter(SocialServiceInternal, "providers", function () { - initService(); - let providers = {}; - for (let manifest of this.manifests) { - try { - if (ActiveProviders.has(manifest.origin)) { - let provider = new SocialProvider(manifest); - providers[provider.origin] = provider; - } - } catch (err) { - Cu.reportError("SocialService: failed to load provider: " + manifest.origin + - ", exception: " + err); - } - } - return providers; -}); - function schedule(callback) { Services.tm.mainThread.dispatch(callback, Ci.nsIThread.DISPATCH_NORMAL); } // Public API this.SocialService = { get enabled() { return SocialServiceInternal.enabled; @@ -439,30 +458,18 @@ this.SocialService = { }); }, // Returns an array of installed providers, sorted by frecency getOrderedProviderList: function(onDone) { SocialServiceInternal.orderedProviders(onDone); }, - getOriginActivationType: function(origin) { - let prefname = SocialServiceInternal.getManifestPrefname(origin); - if (Services.prefs.getDefaultBranch("social.manifest.").getPrefType(prefname) == Services.prefs.PREF_STRING) - return 'builtin'; - - let whitelist = Services.prefs.getCharPref("social.whitelist").split(','); - if (whitelist.indexOf(origin) >= 0) - return 'whitelist'; - - let directories = Services.prefs.getCharPref("social.directories").split(','); - if (directories.indexOf(origin) >= 0) - return 'directory'; - - return 'foreign'; + getOriginActivationType: function (origin) { + return getOriginActivationType(origin); }, _providerListeners: new Map(), registerProviderListener: function registerProviderListener(listener) { this._providerListeners.set(listener, 1); }, unregisterProviderListener: function unregisterProviderListener(listener) { this._providerListeners.delete(listener); @@ -580,17 +587,17 @@ this.SocialService = { }.bind(this)); }.bind(this)); }, _installProvider: function(aDOMDocument, data, installCallback) { let sourceURI = aDOMDocument.location.href; let installOrigin = aDOMDocument.nodePrincipal.origin; - let installType = this.getOriginActivationType(installOrigin); + let installType = getOriginActivationType(installOrigin); let manifest; if (data) { // if we get data, we MUST have a valid manifest generated from the data manifest = this._manifestFromData(installType, data, aDOMDocument.nodePrincipal); if (!manifest) throw new Error("SocialService.installProvider: service configuration is invalid from " + sourceURI); } let installer; @@ -644,18 +651,19 @@ this.SocialService = { }; /** * The SocialProvider object represents a social provider, and allows * access to its FrameWorker (if it has one). * * @constructor * @param {jsobj} object representing the manifest file describing this provider + * @param {bool} boolean indicating whether this provider is "built in" */ -function SocialProvider(input) { +function SocialProvider(input, blessed = false) { if (!input.name) throw new Error("SocialProvider must be passed a name"); if (!input.origin) throw new Error("SocialProvider must be passed an origin"); let id = getAddonIDFromOrigin(input.origin); if (Services.blocklist.getAddonBlocklistState(id, input.version || "0") == Ci.nsIBlocklistService.STATE_BLOCKED) throw new Error("SocialProvider: provider with origin [" + @@ -669,16 +677,17 @@ function SocialProvider(input) { this.sidebarURL = input.sidebarURL; this.shareURL = input.shareURL; this.origin = input.origin; let originUri = Services.io.newURI(input.origin, null, null); this.principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(originUri); this.ambientNotificationIcons = {}; this.errorState = null; this.frecency = 0; + this.blessed = blessed; try { this.domain = etld.getBaseDomainFromHost(originUri.host); } catch(e) { this.domain = originUri.host; } } SocialProvider.prototype = { @@ -864,18 +873,22 @@ SocialProvider.prototype = { * * Returns null if this provider has no workerURL, or is disabled. * * @param {DOMWindow} window (optional) */ getWorkerPort: function getWorkerPort(window) { if (!this.workerURL || !this.enabled) return null; - return getFrameWorkerHandle(this.workerURL, window, - "SocialProvider:" + this.origin, this.origin).port; + // Only allow localStorage in the frameworker for blessed providers + let allowLocalStorage = this.blessed; + let handle = getFrameWorkerHandle(this.workerURL, window, + "SocialProvider:" + this.origin, this.origin, + allowLocalStorage); + return handle.port; }, /** * Checks if a given URI is of the same origin as the provider. * * Returns true or false. * * @param {URI or string} uri
--- a/toolkit/components/social/test/browser/browser_frameworker.js +++ b/toolkit/components/social/test/browser/browser_frameworker.js @@ -236,26 +236,50 @@ let tests = { ok = localStorage["foo"] == 1; } catch (e) { port.postMessage({topic: "done", result: "FAILED to read localStorage, " + e.toString() }); return; } port.postMessage({topic: "done", result: "ok"}); } } - let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testLocalStorage"); + let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testLocalStorage", null, true); worker.port.onmessage = function(e) { if (e.data.topic == "done") { is(e.data.result, "ok", "check the localStorage test worked"); worker.terminate(); cbnext(); } } }, + testNoLocalStorage: function(cbnext) { + let run = function() { + onconnect = function(e) { + let port = e.ports[0]; + try { + localStorage.setItem("foo", "1"); + } catch(e) { + port.postMessage({topic: "done", result: "ok"}); + return; + } + + port.postMessage({topic: "done", result: "FAILED because localStorage was exposed" }); + } + } + let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testNoLocalStorage"); + worker.port.onmessage = function(e) { + if (e.data.topic == "done") { + is(e.data.result, "ok", "check that retrieving localStorage fails by default"); + worker.terminate(); + cbnext(); + } + } + }, + testBase64: function (cbnext) { let run = function() { onconnect = function(e) { let port = e.ports[0]; var ok = false; try { ok = btoa("1234") == "MTIzNA=="; } catch(e) {
--- a/toolkit/crashreporter/google-breakpad/src/common/Makefile.in +++ b/toolkit/crashreporter/google-breakpad/src/common/Makefile.in @@ -74,17 +74,16 @@ OS_CXXFLAGS += -DHAVE_A_OUT_H endif ifeq (Darwin,$(OS_ARCH)) HOST_CXXFLAGS += -DHAVE_MACH_O_NLIST_H OS_CXXFLAGS += -DHAVE_MACH_O_NLIST_H endif ifneq (WINNT,$(OS_TARGET)) ifdef MOZ_CRASHREPORTER -DISABLED_HOST_CSRCS = $(CSRCS) endif endif # need static lib FORCE_STATIC_LIB = 1 include $(topsrcdir)/config/rules.mk
--- a/toolkit/mozapps/installer/packager.mk +++ b/toolkit/mozapps/installer/packager.mk @@ -242,20 +242,22 @@ INNER_UNMAKE_PACKAGE = $(error Try using endif #Create an RPM file ifeq ($(MOZ_PKG_FORMAT),APK) JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar include $(MOZILLA_DIR)/config/android-common.mk +# DEBUG_JARSIGNER is defined by android-common.mk and always debug +# signs. We want to release sign if possible. ifdef MOZ_SIGN_CMD -JARSIGNER := $(MOZ_SIGN_CMD) -f jar +RELEASE_JARSIGNER := $(MOZ_SIGN_CMD) -f jar else -JARSIGNER ?= echo +RELEASE_JARSIGNER := $(DEBUG_JARSIGNER) endif DIST_FILES = # Place the files in the order they are going to be opened by the linker DIST_FILES += libmozalloc.so ifndef MOZ_FOLD_LIBS DIST_FILES += \ @@ -322,20 +324,22 @@ endif GECKO_APP_AP_PATH = $(call core_abspath,$(DEPTH)/mobile/android/base) ifdef ENABLE_TESTS INNER_ROBOCOP_PACKAGE=echo ifeq ($(MOZ_BUILD_APP),mobile/android) UPLOAD_EXTRA_FILES += robocop.apk UPLOAD_EXTRA_FILES += fennec_ids.txt ROBOCOP_PATH = $(call core_abspath,$(_ABS_DIST)/../build/mobile/robocop) +# Robocop and Fennec need to be signed with the same key, which means +# release signing them both. INNER_ROBOCOP_PACKAGE= \ $(NSINSTALL) $(GECKO_APP_AP_PATH)/fennec_ids.txt $(_ABS_DIST) && \ - cp $(ROBOCOP_PATH)/robocop-debug-signed-unaligned.apk $(_ABS_DIST)/robocop-unaligned.apk && \ - $(JARSIGNER) $(_ABS_DIST)/robocop-unaligned.apk && \ + cp $(ROBOCOP_PATH)/robocop-debug-unsigned-unaligned.apk $(_ABS_DIST)/robocop-unaligned.apk && \ + $(RELEASE_JARSIGNER) $(_ABS_DIST)/robocop-unaligned.apk && \ $(ZIPALIGN) -f -v 4 $(_ABS_DIST)/robocop-unaligned.apk $(_ABS_DIST)/robocop.apk endif else INNER_ROBOCOP_PACKAGE=echo 'Testing is disabled - No Robocop for you' endif ifdef MOZ_OMX_PLUGIN DIST_FILES += libomxplugin.so libomxplugingb.so libomxplugingb235.so libomxpluginhc.so libomxpluginsony.so libomxpluginfroyo.so libomxpluginjb-htc.so @@ -365,19 +369,20 @@ INNER_MAKE_PACKAGE = \ mv libmozglue.so $(MOZ_CHILD_PROCESS_NAME) lib/$(ABI_DIR) && \ mv $(SO_LIBRARIES) assets && \ unzip -o $(_ABS_DIST)/gecko.ap_ && \ rm $(_ABS_DIST)/gecko.ap_ && \ $(ZIP) -0 $(_ABS_DIST)/gecko.ap_ $(ASSET_SO_LIBRARIES) && \ $(ZIP) -r9D $(_ABS_DIST)/gecko.ap_ $(DIST_FILES) -x $(NON_DIST_FILES) $(SZIP_LIBRARIES) && \ $(ZIP) -0 $(_ABS_DIST)/gecko.ap_ $(OMNIJAR_NAME)) && \ rm -f $(_ABS_DIST)/gecko.apk && \ - $(APKBUILDER) $(_ABS_DIST)/gecko.apk -v $(APKBUILDER_FLAGS) -z $(_ABS_DIST)/gecko.ap_ -f $(STAGEPATH)$(MOZ_PKG_DIR)$(_BINPATH)/classes.dex && \ + cp $(_ABS_DIST)/gecko.ap_ $(_ABS_DIST)/gecko.apk && \ + $(ZIP) -vj0 $(_ABS_DIST)/gecko.apk $(STAGEPATH)$(MOZ_PKG_DIR)$(_BINPATH)/classes.dex && \ cp $(_ABS_DIST)/gecko.apk $(_ABS_DIST)/gecko-unsigned-unaligned.apk && \ - $(JARSIGNER) $(_ABS_DIST)/gecko.apk && \ + $(RELEASE_JARSIGNER) $(_ABS_DIST)/gecko.apk && \ $(ZIPALIGN) -f -v 4 $(_ABS_DIST)/gecko.apk $(PACKAGE) && \ $(INNER_ROBOCOP_PACKAGE) INNER_UNMAKE_PACKAGE = \ mkdir $(MOZ_PKG_DIR) && \ ( cd $(MOZ_PKG_DIR) && \ $(UNZIP) $(UNPACKAGE) && \ mv lib/$(ABI_DIR)/libmozglue.so . && \
--- a/toolkit/mozapps/installer/windows/nsis/common.nsh +++ b/toolkit/mozapps/installer/windows/nsis/common.nsh @@ -1148,16 +1148,18 @@ * @param _VALICON * The path to the binary that contains the icon group for the default icon * followed by a comma and either the icon group's resource index or the icon * group's resource id prefixed with a minus sign * @param _DISPNAME * The display name for the handler. If emtpy no value will be set. * @param _ISPROTOCOL * Sets protocol handler specific registry values when "true". + * Deletes protocol handler specific registry values when "delete". + * Otherwise doesn't touch handler specific registry values. * @param _ISDDE * Sets DDE specific registry values when "true". * * $R3 = string value of the current registry key path. * $R4 = _KEY * $R5 = _VALOPEN * $R6 = _VALICON * $R7 = _DISPNAME @@ -1189,16 +1191,18 @@ ReadRegStr $R3 SHCTX "$R4" "FriendlyTypeName" StrCmp "$R3" "" +1 +3 WriteRegStr SHCTX "$R4" "" "$R7" WriteRegStr SHCTX "$R4" "FriendlyTypeName" "$R7" StrCmp "$R8" "true" +1 +2 WriteRegStr SHCTX "$R4" "URL Protocol" "" + StrCmp "$R8" "delete" +1 +2 + DeleteRegValue SHCTX "$R4" "URL Protocol" StrCpy $R3 "" ReadRegDWord $R3 SHCTX "$R4" "EditFlags" StrCmp $R3 "" +1 +3 ; Only add EditFlags if a value doesn't exist DeleteRegValue SHCTX "$R4" "EditFlags" WriteRegDWord SHCTX "$R4" "EditFlags" 0x00000002 StrCmp "$R6" "" +2 +1 WriteRegStr SHCTX "$R4\DefaultIcon" "" "$R6" @@ -1292,16 +1296,18 @@ * @param _VALICON * The path to the binary that contains the icon group for the default icon * followed by a comma and either the icon group's resource index or the icon * group's resource id prefixed with a minus sign * @param _DISPNAME * The display name for the handler. If emtpy no value will be set. * @param _ISPROTOCOL * Sets protocol handler specific registry values when "true". + * Deletes protocol handler specific registry values when "delete". + * Otherwise doesn't touch handler specific registry values. * @param _DDE_APPNAME * Sets DDE specific registry values when not an empty string. * * $R0 = storage for SOFTWARE\Classes * $R1 = string value of the current registry key path. * $R2 = _KEY * $R3 = _VALOPEN * $R4 = _VALICON @@ -1342,16 +1348,18 @@ ReadRegStr $R1 SHCTX "$R2" "FriendlyTypeName" StrCmp "$R1" "" +1 +3 WriteRegStr SHCTX "$R0\$R2" "" "$R5" WriteRegStr SHCTX "$R0\$R2" "FriendlyTypeName" "$R5" StrCmp "$R6" "true" +1 +2 WriteRegStr SHCTX "$R0\$R2" "URL Protocol" "" + StrCmp "$R6" "delete" +1 +2 + DeleteRegValue SHCTX "$R0\$R2" "URL Protocol" StrCpy $R1 "" ReadRegDWord $R1 SHCTX "$R0\$R2" "EditFlags" StrCmp $R1 "" +1 +3 ; Only add EditFlags if a value doesn't exist DeleteRegValue SHCTX "$R0\$R2" "EditFlags" WriteRegDWord SHCTX "$R0\$R2" "EditFlags" 0x00000002 StrCmp "$R4" "" +2 +1 WriteRegStr SHCTX "$R0\$R2\DefaultIcon" "" "$R4" @@ -1451,16 +1459,18 @@ * @param _VALICON * The path to the binary that contains the icon group for the default icon * followed by a comma and either the icon group's resource index or the icon * group's resource id prefixed with a minus sign * @param _DISPNAME * The display name for the handler. If emtpy no value will be set. * @param _ISPROTOCOL * Sets protocol handler specific registry values when "true". + * Deletes protocol handler specific registry values when "delete". + * Otherwise doesn't touch handler specific registry values. * * $R3 = storage for SOFTWARE\Classes * $R4 = string value of the current registry key path. * $R5 = _KEY * $R6 = _VALOPEN * $R7 = _VALICON * $R8 = _DISPNAME * $R9 = _ISPROTOCOL @@ -1468,17 +1478,17 @@ !macro AddDisabledDDEHandlerValues !ifndef ${_MOZFUNC_UN}AddDisabledDDEHandlerValues !verbose push !verbose ${_MOZFUNC_VERBOSE} !define ${_MOZFUNC_UN}AddDisabledDDEHandlerValues "!insertmacro ${_MOZFUNC_UN}AddDisabledDDEHandlerValuesCall" Function ${_MOZFUNC_UN}AddDisabledDDEHandlerValues - Exch $R9 ; true if a protocol handler + Exch $R9 ; _ISPROTOCOL Exch 1 Exch $R8 ; FriendlyTypeName Exch 2 Exch $R7 ; icon index Exch 3 Exch $R6 ; shell\open\command Exch 4 Exch $R5 ; reg key
--- a/uriloader/exthandler/tests/mochitest/Makefile.in +++ b/uriloader/exthandler/tests/mochitest/Makefile.in @@ -11,11 +11,12 @@ relativesrcdir = @relativesrcdir@ include $(DEPTH)/config/autoconf.mk MOCHITEST_FILES = \ test_handlerApps.xhtml \ test_unsafeBidiChars.xhtml \ handlerApps.js \ handlerApp.xhtml \ unsafeBidiFileName.sjs \ + test_badMimeType.html \ $(NULL) include $(topsrcdir)/config/rules.mk
new file mode 100644 --- /dev/null +++ b/uriloader/exthandler/tests/mochitest/test_badMimeType.html @@ -0,0 +1,27 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Crashtest for bad MIME type</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<pre id="test"> +<script class="testbody" type="text/javascript"> + const Cc = SpecialPowers.Cc; + const Ci = SpecialPowers.Ci; + const mimeService = Cc["@mozilla.org/mime;1"]. + getService(Ci.nsIMIMEService); + // "text/plain" has an 0xFF character appended to it. This means it's an + // invalid string, which is tricky to enter using a text editor (I used + // emacs' hexl-mode). It also means an ordinary text editor might drop it + // or convert it to something that *is* valid (in UTF8). So we measure + // its length to make sure this hasn't happened. + var badMimeType = "text/plainÿ"; + ok(badMimeType.length == 11, "badMimeType has changed, making this test invalid"); + mimeService.getFromTypeAndExtension(badMimeType, "txt"); + ok(true, "The test shouldn't trigger a crash"); +</script> +</pre> +</body> +</html>
--- a/view/public/nsViewManager.h +++ b/view/public/nsViewManager.h @@ -339,20 +339,16 @@ private: void ReparentWidgets(nsView* aView, nsView *aParent); void InvalidateWidgetArea(nsView *aWidgetView, const nsRegion &aDamagedRegion); void InvalidateViews(nsView *aView); // aView is the view for aWidget and aRegion is relative to aWidget. void Refresh(nsView *aView, const nsIntRegion& aRegion); - void InvalidateRectDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut); - void InvalidateHorizontalBandDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut, - nscoord aY1, nscoord aY2, bool aInCutOut); - // Utilities bool IsViewInserted(nsView *aView); /** * Intersects aRect with aView's bounds and then transforms it from aView's * coordinate system to the coordinate system of the widget attached to * aView.
--- a/view/src/nsViewManager.cpp +++ b/view/src/nsViewManager.cpp @@ -20,16 +20,17 @@ #include "nsPresContext.h" #include "nsEventStateManager.h" #include "mozilla/StartupTimeline.h" #include "GeckoProfiler.h" #include "nsRefreshDriver.h" #include "mozilla/Preferences.h" #include "nsContentUtils.h" #include "nsLayoutUtils.h" +#include "mozilla/layers/Compositor.h" /** XXX TODO XXX DeCOMify newly private methods Optimize view storage */ @@ -37,16 +38,18 @@ A note about platform assumptions: We assume that a widget is z-ordered on top of its parent. We do NOT assume anything about the relative z-ordering of sibling widgets. Even though we ask for a specific z-order, we don't assume that widget z-ordering actually works. */ +using namespace mozilla::layers; + #define NSCOORD_NONE INT32_MIN #undef DEBUG_MOUSE_LOCATION int32_t nsViewManager::mVMCount = 0; // Weakly held references to all of the view managers nsVoidArray* nsViewManager::gViewManagers = nullptr; @@ -325,18 +328,24 @@ void nsViewManager::Refresh(nsView *aVie "Widgets that we paint must all be display roots"); if (mPresShell) { #ifdef MOZ_DUMP_PAINTING if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { printf("--COMPOSITE-- %p\n", mPresShell); } #endif - mPresShell->Paint(aView, damageRegion, - nsIPresShell::PAINT_COMPOSITE); + uint32_t paintFlags = nsIPresShell::PAINT_COMPOSITE; + LayerManager *manager = widget->GetLayerManager(); + if (!manager->NeedsWidgetInvalidation()) { + manager->FlushRendering(); + } else { + mPresShell->Paint(aView, damageRegion, + paintFlags); + } #ifdef MOZ_DUMP_PAINTING if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { printf("--ENDCOMPOSITE--\n"); } #endif mozilla::StartupTimeline::RecordOnce(mozilla::StartupTimeline::FIRST_PAINT); } @@ -379,17 +388,16 @@ void nsViewManager::ProcessPendingUpdate for (nsViewManager *vm = this; vm; vm = vm->mRootView->GetParent() ? vm->mRootView->GetParent()->GetViewManager() : nullptr) { if (vm->mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) && vm->mRootView->IsEffectivelyVisible() && mPresShell && mPresShell->IsVisible()) { vm->FlushDelayedResize(true); - vm->InvalidateView(vm->mRootView); } } NS_ASSERTION(aView->HasWidget(), "Must have a widget!"); #ifdef MOZ_DUMP_PAINTING if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { printf("---- PAINT START ----PresShell(%p), nsView(%p), nsIWidget(%p)\n", mPresShell, aView, widget); @@ -837,21 +845,16 @@ nsViewManager::InsertChild(nsView *aPare aParent->InsertChild(aChild, kid); ReparentWidgets(aChild, aParent); } } // if the parent view is marked as "floating", make the newly added view float as well. if (aParent->GetFloating()) aChild->SetFloating(true); - - //and mark this area as dirty if the view is visible... - - if (nsViewVisibility_kHide != aChild->GetVisibility()) - aChild->GetViewManager()->InvalidateView(aChild); } } void nsViewManager::InsertChild(nsView *aParent, nsView *aChild, int32_t aZIndex) { // no-one really calls this with anything other than aZIndex == 0 on a fresh view // XXX this method should simply be eliminated and its callers redirected to the real method @@ -864,103 +867,35 @@ nsViewManager::RemoveChild(nsView *aChil { NS_ASSERTION(aChild, "aChild must not be null"); nsView* parent = aChild->GetParent(); if (nullptr != parent) { NS_ASSERTION(aChild->GetViewManager() == this || parent->GetViewManager() == this, "wrong view manager"); - aChild->GetViewManager()->InvalidateView(aChild); parent->RemoveChild(aChild); } } void nsViewManager::MoveViewTo(nsView *aView, nscoord aX, nscoord aY) { NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager"); - nsPoint oldPt = aView->GetPosition(); - nsRect oldBounds = aView->GetBoundsInParentUnits(); aView->SetPosition(aX, aY); - - // only do damage control if the view is visible - - if ((aX != oldPt.x) || (aY != oldPt.y)) { - if (aView->GetVisibility() != nsViewVisibility_kHide) { - nsView* parentView = aView->GetParent(); - if (parentView) { - nsViewManager* parentVM = parentView->GetViewManager(); - parentVM->InvalidateView(parentView, oldBounds); - parentVM->InvalidateView(parentView, aView->GetBoundsInParentUnits()); - } - } - } -} - -void nsViewManager::InvalidateHorizontalBandDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut, - nscoord aY1, nscoord aY2, bool aInCutOut) { - nscoord height = aY2 - aY1; - if (aRect.x < aCutOut.x) { - nsRect r(aRect.x, aY1, aCutOut.x - aRect.x, height); - InvalidateView(aView, r); - } - if (!aInCutOut && aCutOut.x < aCutOut.XMost()) { - nsRect r(aCutOut.x, aY1, aCutOut.width, height); - InvalidateView(aView, r); - } - if (aCutOut.XMost() < aRect.XMost()) { - nsRect r(aCutOut.XMost(), aY1, aRect.XMost() - aCutOut.XMost(), height); - InvalidateView(aView, r); - } -} - -void nsViewManager::InvalidateRectDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut) { - NS_ASSERTION(aView->GetViewManager() == this, - "InvalidateRectDifference called on view we don't own"); - if (aRect.y < aCutOut.y) { - InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aRect.y, aCutOut.y, false); - } - if (aCutOut.y < aCutOut.YMost()) { - InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aCutOut.y, aCutOut.YMost(), true); - } - if (aCutOut.YMost() < aRect.YMost()) { - InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aCutOut.YMost(), aRect.YMost(), false); - } } void nsViewManager::ResizeView(nsView *aView, const nsRect &aRect, bool aRepaintExposedAreaOnly) { NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager"); nsRect oldDimensions = aView->GetDimensions(); if (!oldDimensions.IsEqualEdges(aRect)) { - // resize the view. - // Prevent Invalidation of hidden views - if (aView->GetVisibility() == nsViewVisibility_kHide) { - aView->SetDimensions(aRect, false); - } else { - nsView* parentView = aView->GetParent(); - if (!parentView) { - parentView = aView; - } - nsRect oldBounds = aView->GetBoundsInParentUnits(); - aView->SetDimensions(aRect, true); - nsViewManager* parentVM = parentView->GetViewManager(); - if (!aRepaintExposedAreaOnly) { - // Invalidate the union of the old and new size - InvalidateView(aView, aRect); - parentVM->InvalidateView(parentView, oldBounds); - } else { - InvalidateRectDifference(aView, aRect, oldDimensions); - nsRect newBounds = aView->GetBoundsInParentUnits(); - parentVM->InvalidateRectDifference(parentView, oldBounds, newBounds); - } - } + aView->SetDimensions(aRect, true); } // Note that if layout resizes the view and the view has a custom clip // region set, then we expect layout to update the clip region too. Thus // in the case where mClipRect has been optimized away to just be a null // pointer, and this resize is implicitly changing the clip rect, it's OK // because layout will change it back again if necessary. } @@ -975,31 +910,16 @@ nsViewManager::SetViewFloating(nsView *a void nsViewManager::SetViewVisibility(nsView *aView, nsViewVisibility aVisible) { NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager"); if (aVisible != aView->GetVisibility()) { aView->SetVisibility(aVisible); - - if (IsViewInserted(aView)) { - if (!aView->HasWidget()) { - if (nsViewVisibility_kHide == aVisible) { - nsView* parentView = aView->GetParent(); - if (parentView) { - parentView->GetViewManager()-> - InvalidateView(parentView, aView->GetBoundsInParentUnits()); - } - } - else { - InvalidateView(aView); - } - } - } } } bool nsViewManager::IsViewInserted(nsView *aView) { if (mRootView == aView) { return true; } else if (aView->GetParent() == nullptr) { @@ -1022,30 +942,21 @@ nsViewManager::SetViewZIndex(nsView *aVi NS_ASSERTION((aView != nullptr), "no view"); // don't allow the root view's z-index to be changed. It should always be zero. // This could be removed and replaced with a style rule, or just removed altogether, with interesting consequences if (aView == mRootView) { return; } - bool oldTopMost = aView->IsTopMost(); - bool oldIsAuto = aView->GetZIndexIsAuto(); - if (aAutoZIndex) { aZIndex = 0; } - int32_t oldidx = aView->GetZIndex(); aView->SetZIndex(aAutoZIndex, aZIndex, aTopMost); - - if (oldidx != aZIndex || oldTopMost != aTopMost || - oldIsAuto != aAutoZIndex) { - InvalidateView(aView); - } } nsViewManager* nsViewManager::IncrementDisableRefreshCount() { if (!IsRootVM()) { return RootViewManager()->IncrementDisableRefreshCount(); }
--- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -1381,16 +1381,20 @@ static void blinkRgn(RgnHandle rgn) // Invalidate this component's visible area NS_IMETHODIMP nsChildView::Invalidate(const nsIntRect &aRect) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; if (!mView || !mVisible) return NS_OK; + NS_ASSERTION(GetLayerManager()->GetBackendType() != LAYERS_CLIENT || + Compositor::GetBackend() == LAYERS_BASIC, + "Shouldn't need to invalidate with accelerated OMTC layers!"); + if ([NSView focusView]) { // if a view is focussed (i.e. being drawn), then postpone the invalidate so that we // don't lose it. [mView setNeedsPendingDisplayInRect:DevPixelsToCocoaPoints(aRect)]; } else { [mView setNeedsDisplayInRect:DevPixelsToCocoaPoints(aRect)]; }
--- a/xpcom/glue/Makefile.in +++ b/xpcom/glue/Makefile.in @@ -41,16 +41,17 @@ SDK_HEADERS = \ nsIClassInfoImpl.h \ nsID.h \ nsIInterfaceRequestorUtils.h \ nsINIParser.h \ nsISupportsImpl.h \ nsISupportsUtils.h \ nsIWeakReferenceUtils.h \ nsInterfaceHashtable.h \ + nsJSThingHashtable.h \ nsMemory.h \ nsQuickSort.h \ nsRefPtrHashtable.h \ nsServiceManagerUtils.h \ nsStringAPI.h \ nsStringGlue.h \ nsTArray.h \ nsTArray-inl.h \
--- a/xpcom/glue/nsCycleCollectionParticipant.cpp +++ b/xpcom/glue/nsCycleCollectionParticipant.cpp @@ -64,39 +64,39 @@ CycleCollectionNoteEdgeNameImpl(nsCycleC nsAutoCString arrayEdgeName(aName); if (aFlags & CycleCollectionEdgeNameArrayFlag) { arrayEdgeName.AppendLiteral("[i]"); } aCallback.NoteNextEdgeName(arrayEdgeName.get()); } void -TraceCallbackFunc::Trace(JS::Value* p, const char* name, void* closure) const +TraceCallbackFunc::Trace(JS::Heap<JS::Value>* p, const char* name, void* closure) const { - mCallback(JSVAL_TO_TRACEABLE(*p), name, closure); + mCallback(JSVAL_TO_TRACEABLE(p->get()), name, closure); } void -TraceCallbackFunc::Trace(jsid* p, const char* name, void* closure) const +TraceCallbackFunc::Trace(JS::Heap<jsid>* p, const char* name, void* closure) const { void *thing = JSID_TO_GCTHING(*p); if (thing) { mCallback(thing, name, closure); } } void -TraceCallbackFunc::Trace(JSObject** p, const char* name, void* closure) const +TraceCallbackFunc::Trace(JS::Heap<JSObject*>* p, const char* name, void* closure) const { mCallback(*p, name, closure); } void -TraceCallbackFunc::Trace(JSString** p, const char* name, void* closure) const +TraceCallbackFunc::Trace(JS::Heap<JSString*>* p, const char* name, void* closure) const { mCallback(*p, name, closure); } void -TraceCallbackFunc::Trace(JSScript** p, const char* name, void* closure) const +TraceCallbackFunc::Trace(JS::Heap<JSScript*>* p, const char* name, void* closure) const { - mCallback(*p, name, closure); + mCallback(p->get(), name, closure); }
--- a/xpcom/glue/nsCycleCollectionParticipant.h +++ b/xpcom/glue/nsCycleCollectionParticipant.h @@ -46,49 +46,49 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsCycleCol /** * Forward declarations */ class nsCycleCollectionParticipant; class nsScriptObjectTracer; class nsXPCOMCycleCollectionParticipant; +namespace JS { +template <class T> class Heap; +} /* namespace JS */ + /* * A struct defining pure virtual methods which are called when tracing cycle * collection paticipants. The appropriate method is called depending on the * type of JS GC thing. */ struct TraceCallbacks { - virtual void Trace(JS::Value* p, const char* name, void* closure) const = 0; - virtual void Trace(jsid* p, const char* name, void* closure) const = 0; - virtual void Trace(JSObject** p, const char* name, void* closure) const = 0; - virtual void Trace(JSString** p, const char* name, void* closure) const = 0; - virtual void Trace(JSScript** p, const char* name, void* closure) const = 0; - - void Trace(JSFlatString** p, const char* name, void* closure) const { - Trace(reinterpret_cast<JSString**>(p), name, closure); - } + virtual void Trace(JS::Heap<JS::Value>* p, const char* name, void* closure) const = 0; + virtual void Trace(JS::Heap<jsid>* p, const char* name, void* closure) const = 0; + virtual void Trace(JS::Heap<JSObject*>* p, const char* name, void* closure) const = 0; + virtual void Trace(JS::Heap<JSString*>* p, const char* name, void* closure) const = 0; + virtual void Trace(JS::Heap<JSScript*>* p, const char* name, void* closure) const = 0; }; /* * An implementation of TraceCallbacks that calls a single function for all JS * GC thing types encountered. */ struct TraceCallbackFunc : public TraceCallbacks { typedef void (* Func)(void* p, const char* name, void* closure); explicit TraceCallbackFunc(Func cb) : mCallback(cb) {} - virtual void Trace(JS::Value* p, const char* name, void* closure) const MOZ_OVERRIDE; - virtual void Trace(jsid* p, const char* name, void* closure) const MOZ_OVERRIDE; - virtual void Trace(JSObject** p, const char* name, void* closure) const MOZ_OVERRIDE; - virtual void Trace(JSString** p, const char* name, void* closure) const MOZ_OVERRIDE; - virtual void Trace(JSScript** p, const char* name, void* closure) const MOZ_OVERRIDE; + virtual void Trace(JS::Heap<JS::Value>* p, const char* name, void* closure) const MOZ_OVERRIDE; + virtual void Trace(JS::Heap<jsid>* p, const char* name, void* closure) const MOZ_OVERRIDE; + virtual void Trace(JS::Heap<JSObject*>* p, const char* name, void* closure) const MOZ_OVERRIDE; + virtual void Trace(JS::Heap<JSString*>* p, const char* name, void* closure) const MOZ_OVERRIDE; + virtual void Trace(JS::Heap<JSScript*>* p, const char* name, void* closure) const MOZ_OVERRIDE; private: Func mCallback; }; /** * VTables *
new file mode 100644 --- /dev/null +++ b/xpcom/glue/nsJSThingHashtable.h @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsJSThingHashtable_h__ +#define nsJSThingHashtable_h__ + +#include "nsHashKeys.h" +#include "nsBaseHashtable.h" + +namespace JS { +template <class T> +class Heap; +} /* namespace JS */ + +/** + * A wrapper for hash keys that sets ALLOW_MEMMOVE to false. + * + * This is used in the implementation of nsJSThingHashtable and is not intended + * to be used directly. + * + * It is necessary for hash tables containing JS::Heap<T> values as these must + * be copied rather than memmoved. + */ +template<class T> +class nsHashKeyDisallowMemmove : public T +{ + public: + nsHashKeyDisallowMemmove(const T& key) : T(key) {} + enum { ALLOW_MEMMOVE = false }; +}; + + +/** + * Templated hashtable class for use on the heap where the values are JS GC things. + * + * Storing JS GC thing pointers on the heap requires wrapping them in a + * JS::Heap<T>, and this class takes care of that while presenting an interface + * in terms of the wrapped type T. + * + * For example, to store a hashtable mapping strings to JSObject pointers, you + * can declare a data member like this: + * + * nsJSThingHashtable<nsStringHashKey, JSObject*> mStringToObjectMap; + * + * See nsBaseHashtable for complete declaration + * @param KeyClass a wrapper-class for the hashtable key, see nsHashKeys.h + * for a complete specification. + * @param DataType the datatype being wrapped, must be a JS GC thing. + * @see nsInterfaceHashtable, nsClassHashtable + */ +template<class KeyClass,class DataType> +class nsJSThingHashtable : + public nsBaseHashtable<nsHashKeyDisallowMemmove<KeyClass>, JS::Heap<DataType>, DataType> +{ }; + +#endif // nsJSThingHashtable_h__
--- a/xpcom/glue/nsTArray-inl.h +++ b/xpcom/glue/nsTArray-inl.h @@ -3,32 +3,32 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef nsTArray_h__ # error "Don't include this file directly" #endif -template<class Alloc> -nsTArray_base<Alloc>::nsTArray_base() +template<class Alloc, class Copy> +nsTArray_base<Alloc, Copy>::nsTArray_base() : mHdr(EmptyHdr()) { MOZ_COUNT_CTOR(nsTArray_base); } -template<class Alloc> -nsTArray_base<Alloc>::~nsTArray_base() { +template<class Alloc, class Copy> +nsTArray_base<Alloc, Copy>::~nsTArray_base() { if (mHdr != EmptyHdr() && !UsesAutoArrayBuffer()) { Alloc::Free(mHdr); } MOZ_COUNT_DTOR(nsTArray_base); } -template<class Alloc> -const nsTArrayHeader* nsTArray_base<Alloc>::GetAutoArrayBufferUnsafe(size_t elemAlign) const { +template<class Alloc, class Copy> +const nsTArrayHeader* nsTArray_base<Alloc, Copy>::GetAutoArrayBufferUnsafe(size_t elemAlign) const { // Assuming |this| points to an nsAutoArray, we want to get a pointer to // mAutoBuf. So just cast |this| to nsAutoArray* and read &mAutoBuf! const void* autoBuf = &reinterpret_cast<const nsAutoArrayBase<nsTArray<uint32_t>, 1>*>(this)->mAutoBuf; // If we're on a 32-bit system and elemAlign is 8, we need to adjust our // pointer to take into account the extra alignment in the auto array. @@ -43,18 +43,18 @@ const nsTArrayHeader* nsTArray_base<Allo NS_ABORT_IF_FALSE(elemAlign <= 4 || elemAlign == 8, "unsupported alignment."); if (sizeof(void*) == 4 && elemAlign == 8) { autoBuf = reinterpret_cast<const char*>(autoBuf) + 4; } return reinterpret_cast<const Header*>(autoBuf); } -template<class Alloc> -bool nsTArray_base<Alloc>::UsesAutoArrayBuffer() const { +template<class Alloc, class Copy> +bool nsTArray_base<Alloc, Copy>::UsesAutoArrayBuffer() const { if (!mHdr->mIsAutoArray) { return false; } // This is nuts. If we were sane, we'd pass elemAlign as a parameter to // this function. Unfortunately this function is called in nsTArray_base's // destructor, at which point we don't know elem_type's alignment. // @@ -91,19 +91,19 @@ bool nsTArray_base<Alloc>::UsesAutoArray reinterpret_cast<const char*>(GetAutoArrayBuffer(4)); NS_ABORT_IF_FALSE(diff >= 0 && diff <= 4, "GetAutoArrayBuffer doesn't do what we expect."); #endif return mHdr == GetAutoArrayBuffer(4) || mHdr == GetAutoArrayBuffer(8); } -template<class Alloc> +template<class Alloc, class Copy> typename Alloc::ResultTypeProxy -nsTArray_base<Alloc>::EnsureCapacity(size_type capacity, size_type elemSize) { +nsTArray_base<Alloc, Copy>::EnsureCapacity(size_type capacity, size_type elemSize) { // This should be the most common case so test this first if (capacity <= mHdr->mCapacity) return Alloc::SuccessResult(); // If the requested memory allocation exceeds size_type(-1)/2, then // our doubling algorithm may not be able to allocate it. // Additionally we couldn't fit in the Header::mCapacity // member. Just bail out in cases like that. We don't want to be @@ -150,23 +150,26 @@ nsTArray_base<Alloc>::EnsureCapacity(siz bytesToAlloc |= bytesToAlloc >> 16; bytesToAlloc++; MOZ_ASSERT((bytesToAlloc & (bytesToAlloc - 1)) == 0, "nsTArray's allocation size should be a power of two!"); } Header *header; - if (UsesAutoArrayBuffer()) { + if (UsesAutoArrayBuffer() || !Copy::allowRealloc) { // Malloc() and copy header = static_cast<Header*>(Alloc::Malloc(bytesToAlloc)); if (!header) return Alloc::FailureResult(); - memcpy(header, mHdr, sizeof(Header) + Length() * elemSize); + Copy::CopyHeaderAndElements(header, mHdr, Length(), elemSize); + + if (!UsesAutoArrayBuffer()) + Alloc::Free(mHdr); } else { // Realloc() existing data header = static_cast<Header*>(Alloc::Realloc(mHdr, bytesToAlloc)); if (!header) return Alloc::FailureResult(); } // How many elements can we fit in bytesToAlloc? @@ -174,33 +177,33 @@ nsTArray_base<Alloc>::EnsureCapacity(siz MOZ_ASSERT(newCapacity >= capacity, "Didn't enlarge the array enough!"); header->mCapacity = newCapacity; mHdr = header; return Alloc::SuccessResult(); } -template<class Alloc> +template<class Alloc, class Copy> void -nsTArray_base<Alloc>::ShrinkCapacity(size_type elemSize, size_t elemAlign) { +nsTArray_base<Alloc, Copy>::ShrinkCapacity(size_type elemSize, size_t elemAlign) { if (mHdr == EmptyHdr() || UsesAutoArrayBuffer()) return; if (mHdr->mLength >= mHdr->mCapacity) // should never be greater than... return; size_type length = Length(); if (IsAutoArray() && GetAutoArrayBuffer(elemAlign)->mCapacity >= length) { Header* header = GetAutoArrayBuffer(elemAlign); // Copy data, but don't copy the header to avoid overwriting mCapacity header->mLength = length; - memcpy(header + 1, mHdr + 1, length * elemSize); + Copy::CopyElements(header + 1, mHdr + 1, length, elemSize); Alloc::Free(mHdr); mHdr = header; return; } if (length == 0) { MOZ_ASSERT(!IsAutoArray(), "autoarray should have fit 0 elements"); @@ -212,19 +215,19 @@ nsTArray_base<Alloc>::ShrinkCapacity(siz size_type size = sizeof(Header) + length * elemSize; void *ptr = Alloc::Realloc(mHdr, size); if (!ptr) return; mHdr = static_cast<Header*>(ptr); mHdr->mCapacity = length; } -template<class Alloc> +template<class Alloc, class Copy> void -nsTArray_base<Alloc>::ShiftData(index_type start, +nsTArray_base<Alloc, Copy>::ShiftData(index_type start, size_type oldLen, size_type newLen, size_type elemSize, size_t elemAlign) { if (oldLen == newLen) return; // Determine how many elements need to be shifted size_type num = mHdr->mLength - (start + oldLen); @@ -235,88 +238,86 @@ nsTArray_base<Alloc>::ShiftData(index_ty } else { // Maybe nothing needs to be shifted if (num == 0) return; // Perform shift (change units to bytes first) start *= elemSize; newLen *= elemSize; oldLen *= elemSize; - num *= elemSize; char *base = reinterpret_cast<char*>(mHdr + 1) + start; - memmove(base + newLen, base + oldLen, num); + Copy::MoveElements(base + newLen, base + oldLen, num, elemSize); } } -template<class Alloc> +template<class Alloc, class Copy> bool -nsTArray_base<Alloc>::InsertSlotsAt(index_type index, size_type count, +nsTArray_base<Alloc, Copy>::InsertSlotsAt(index_type index, size_type count, size_type elementSize, size_t elemAlign) { MOZ_ASSERT(index <= Length(), "Bogus insertion index"); size_type newLen = Length() + count; EnsureCapacity(newLen, elementSize); // Check for out of memory conditions if (Capacity() < newLen) return false; // Move the existing elements as needed. Note that this will // change our mLength, so no need to call IncrementLength. ShiftData(index, 0, count, elementSize, elemAlign); - + return true; } // nsTArray_base::IsAutoArrayRestorer is an RAII class which takes // |nsTArray_base &array| in its constructor. When it's destructed, it ensures // that // // * array.mIsAutoArray has the same value as it did when we started, and // * if array has an auto buffer and mHdr would otherwise point to sEmptyHdr, // array.mHdr points to array's auto buffer. -template<class Alloc> -nsTArray_base<Alloc>::IsAutoArrayRestorer::IsAutoArrayRestorer( - nsTArray_base<Alloc> &array, - size_t elemAlign) +template<class Alloc, class Copy> +nsTArray_base<Alloc, Copy>::IsAutoArrayRestorer::IsAutoArrayRestorer( + nsTArray_base<Alloc, Copy> &array, + size_t elemAlign) : mArray(array), mElemAlign(elemAlign), mIsAuto(array.IsAutoArray()) { } -template<class Alloc> -nsTArray_base<Alloc>::IsAutoArrayRestorer::~IsAutoArrayRestorer() { +template<class Alloc, class Copy> +nsTArray_base<Alloc, Copy>::IsAutoArrayRestorer::~IsAutoArrayRestorer() { // Careful: We don't want to set mIsAutoArray = 1 on sEmptyHdr. if (mIsAuto && mArray.mHdr == mArray.EmptyHdr()) { // Call GetAutoArrayBufferUnsafe() because GetAutoArrayBuffer() asserts // that mHdr->mIsAutoArray is true, which surely isn't the case here. mArray.mHdr = mArray.GetAutoArrayBufferUnsafe(mElemAlign); mArray.mHdr->mLength = 0; } else if (mArray.mHdr != mArray.EmptyHdr()) { mArray.mHdr->mIsAutoArray = mIsAuto; } } -template<class Alloc> +template<class Alloc, class Copy> template<class Allocator> typename Alloc::ResultTypeProxy -nsTArray_base<Alloc>::SwapArrayElements(nsTArray_base<Allocator>& other, - size_type elemSize, - size_t elemAlign) { +nsTArray_base<Alloc, Copy>::SwapArrayElements(nsTArray_base<Allocator, Copy>& other, + size_type elemSize, size_t elemAlign) { // EnsureNotUsingAutoArrayBuffer will set mHdr = sEmptyHdr even if we have an // auto buffer. We need to point mHdr back to our auto buffer before we // return, otherwise we'll forget that we have an auto buffer at all! // IsAutoArrayRestorer takes care of this for us. IsAutoArrayRestorer ourAutoRestorer(*this, elemAlign); - typename nsTArray_base<Allocator>::IsAutoArrayRestorer otherAutoRestorer(other, elemAlign); + typename nsTArray_base<Allocator, Copy>::IsAutoArrayRestorer otherAutoRestorer(other, elemAlign); // If neither array uses an auto buffer which is big enough to store the // other array's elements, then ensure that both arrays use malloc'ed storage // and swap their mHdr pointers. if ((!UsesAutoArrayBuffer() || Capacity() < other.Length()) && (!other.UsesAutoArrayBuffer() || other.Capacity() < Length())) { if (!EnsureNotUsingAutoArrayBuffer(elemSize) || @@ -326,17 +327,17 @@ nsTArray_base<Alloc>::SwapArrayElements( Header *temp = mHdr; mHdr = other.mHdr; other.mHdr = temp; return Alloc::SuccessResult(); } - // Swap the two arrays using memcpy, since at least one is using an auto + // Swap the two arrays by copying, since at least one is using an auto // buffer which is large enough to hold all of the other's elements. We'll // copy the shorter array into temporary storage. // // (We could do better than this in some circumstances. Suppose we're // swapping arrays X and Y. X has space for 2 elements in its auto buffer, // but currently has length 4, so it's using malloc'ed storage. Y has length // 2. When we swap X and Y, we don't need to use a temporary buffer; we can // write Y straight into X's auto buffer, write X's malloc'ed buffer on top @@ -370,34 +371,34 @@ nsTArray_base<Alloc>::SwapArrayElements( // job for AutoTArray! (One of the two arrays we're swapping is using an // auto buffer, so we're likely not allocating a lot of space here. But one // could, in theory, allocate a huge AutoTArray on the heap.) nsAutoArrayBase<nsTArray_Impl<uint8_t, Alloc>, 64> temp; if (!Alloc::Successful(temp.EnsureCapacity(smallerLength, elemSize))) { return Alloc::FailureResult(); } - memcpy(temp.Elements(), smallerElements, smallerLength * elemSize); - memcpy(smallerElements, largerElements, largerLength * elemSize); - memcpy(largerElements, temp.Elements(), smallerLength * elemSize); + Copy::CopyElements(temp.Elements(), smallerElements, smallerLength, elemSize); + Copy::CopyElements(smallerElements, largerElements, largerLength, elemSize); + Copy::CopyElements(largerElements, temp.Elements(), smallerLength, elemSize); // Swap the arrays' lengths. NS_ABORT_IF_FALSE((other.Length() == 0 || mHdr != EmptyHdr()) && (Length() == 0 || other.mHdr != EmptyHdr()), "Don't set sEmptyHdr's length."); size_type tempLength = Length(); mHdr->mLength = other.Length(); other.mHdr->mLength = tempLength; return Alloc::SuccessResult(); } -template<class Alloc> +template<class Alloc, class Copy> bool -nsTArray_base<Alloc>::EnsureNotUsingAutoArrayBuffer(size_type elemSize) { +nsTArray_base<Alloc, Copy>::EnsureNotUsingAutoArrayBuffer(size_type elemSize) { if (UsesAutoArrayBuffer()) { // If you call this on a 0-length array, we'll set that array's mHdr to // sEmptyHdr, in flagrant violation of the nsAutoTArray invariants. It's // up to you to set it back! (If you don't, the nsAutoTArray will forget // that it has an auto buffer.) if (Length() == 0) { mHdr = EmptyHdr(); @@ -405,15 +406,15 @@ nsTArray_base<Alloc>::EnsureNotUsingAuto } size_type size = sizeof(Header) + Length() * elemSize; Header* header = static_cast<Header*>(Alloc::Malloc(size)); if (!header) return false; - memcpy(header, mHdr, size); + Copy::CopyHeaderAndElements(header, mHdr, Length(), elemSize); header->mCapacity = Length(); mHdr = header; } - + return true; }
--- a/xpcom/glue/nsTArray.h +++ b/xpcom/glue/nsTArray.h @@ -17,16 +17,21 @@ #include "nsCycleCollectionNoteChild.h" #include "nsAlgorithm.h" #include "nscore.h" #include "nsQuickSort.h" #include "nsDebug.h" #include "nsTraceRefcnt.h" #include NEW_H +namespace JS { +template <class T> +class Heap; +} /* namespace JS */ + // // nsTArray is a resizable array class, like std::vector. // // Unlike std::vector, which follows C++'s construction/destruction rules, // nsTArray assumes that your "T" can be memmoved()'ed safely. // // The public classes defined in this header are // @@ -51,16 +56,19 @@ // The template parameter (i.e., T in nsTArray<T>) specifies the type of the // elements and has the following requirements: // // T MUST be safely memmove()'able. // T MUST define a copy-constructor. // T MAY define operator< for sorting. // T MAY define operator== for searching. // +// (Note that the memmove requirement may be relaxed for certain types - see +// nsTArray_CopyElements below.) +// // For methods taking a Comparator instance, the Comparator must be a class // defining the following methods: // // class Comparator { // public: // /** @return True if the elements are equals; false otherwise. */ // bool Equals(const elem_type& a, const Item& b) const; // @@ -337,23 +345,23 @@ struct nsTArray_SafeElementAtHelper<nsRe { }; // // This class serves as a base class for nsTArray. It shouldn't be used // directly. It holds common implementation code that does not depend on the // element type of the nsTArray. // -template<class Alloc> +template<class Alloc, class Copy> class nsTArray_base { // Allow swapping elements with |nsTArray_base|s created using a // different allocator. This is kosher because all allocators use // the same free(). - template<class Allocator> + template<class Allocator, class Copier> friend class nsTArray_base; protected: typedef nsTArrayHeader Header; public: typedef uint32_t size_type; typedef uint32_t index_type; @@ -391,17 +399,17 @@ protected: // @param elemSize The size of an array element. // @return False if insufficient memory is available; true otherwise. typename Alloc::ResultTypeProxy EnsureCapacity(size_type capacity, size_type elemSize); // Resize the storage to the minimum required amount. // @param elemSize The size of an array element. // @param elemAlign The alignment in bytes of an array element. void ShrinkCapacity(size_type elemSize, size_t elemAlign); - + // This method may be called to resize a "gap" in the array by shifting // elements around. It updates mLength appropriately. If the resulting // array has zero elements, then the array's memory is free'd. // @param start The starting index of the gap. // @param oldLen The current length of the gap. // @param newLen The desired length of the gap. // @param elemSize The size of an array element. // @param elemAlign The alignment in bytes of an array element. @@ -430,28 +438,28 @@ protected: // @param elementSize the size of an array element. // @param elemAlign the alignment in bytes of an array element. bool InsertSlotsAt(index_type index, size_type count, size_type elementSize, size_t elemAlign); protected: template<class Allocator> typename Alloc::ResultTypeProxy - SwapArrayElements(nsTArray_base<Allocator>& other, + SwapArrayElements(nsTArray_base<Allocator, Copy>& other, size_type elemSize, size_t elemAlign); // This is an RAII class used in SwapArrayElements. class IsAutoArrayRestorer { public: - IsAutoArrayRestorer(nsTArray_base<Alloc> &array, size_t elemAlign); + IsAutoArrayRestorer(nsTArray_base<Alloc, Copy> &array, size_t elemAlign); ~IsAutoArrayRestorer(); private: - nsTArray_base<Alloc> &mArray; + nsTArray_base<Alloc, Copy> &mArray; size_t mElemAlign; bool mIsAuto; }; // Helper function for SwapArrayElements. Ensures that if the array // is an nsAutoTArray that it doesn't use the built-in buffer. bool EnsureNotUsingAutoArrayBuffer(size_type elemSize); @@ -468,30 +476,30 @@ protected: const Header* GetAutoArrayBuffer(size_t elemAlign) const { MOZ_ASSERT(IsAutoArray(), "Should be an auto array to call this"); return GetAutoArrayBufferUnsafe(elemAlign); } // Returns a Header for the built-in buffer of this nsAutoTArray, but doesn't // assert that we are an nsAutoTArray. Header* GetAutoArrayBufferUnsafe(size_t elemAlign) { - return const_cast<Header*>(static_cast<const nsTArray_base<Alloc>*>(this)-> + return const_cast<Header*>(static_cast<const nsTArray_base<Alloc, Copy>*>(this)-> GetAutoArrayBufferUnsafe(elemAlign)); } const Header* GetAutoArrayBufferUnsafe(size_t elemAlign) const; // Returns true if this is an nsAutoTArray and it currently uses the // built-in buffer to store its elements. bool UsesAutoArrayBuffer() const; // The array's elements (prefixed with a Header). This pointer is never // null. If the array is empty, then this will point to sEmptyHdr. Header *mHdr; - Header* Hdr() const { + Header* Hdr() const { return mHdr; } Header** PtrToHdr() { return &mHdr; } static Header* EmptyHdr() { @@ -559,38 +567,172 @@ struct AssignRangeAlgorithm<true, true> template<class Item, class ElemType, class IndexType, class SizeType> static void implementation(ElemType* elements, IndexType start, SizeType count, const Item *values) { memcpy(elements + start, values, count * sizeof(ElemType)); } }; // +// Normally elements are copied with memcpy and memmove, but for some element +// types that is problematic. The nsTArray_CopyElements template class can be +// specialized to ensure that copying calls constructors and destructors +// instead, as is done below for JS::Heap<E> elements. +// + +// +// A class that defines how to copy elements using memcpy/memmove. +// +struct nsTArray_CopyWithMemutils +{ + const static bool allowRealloc = true; + + static void CopyElements(void* dest, const void* src, size_t count, size_t elemSize) { + memcpy(dest, src, count * elemSize); + } + + static void CopyHeaderAndElements(void* dest, const void* src, size_t count, size_t elemSize) { + memcpy(dest, src, sizeof(nsTArrayHeader) + count * elemSize); + } + + static void MoveElements(void* dest, const void* src, size_t count, size_t elemSize) { + memmove(dest, src, count * elemSize); + } +}; + +// +// A template class that defines how to copy elements calling their constructors +// and destructors appropriately. +// +template <class ElemType> +struct nsTArray_CopyWithConstructors +{ + typedef nsTArrayElementTraits<ElemType> traits; + + const static bool allowRealloc = false; + + static void CopyElements(void* dest, void* src, size_t count, size_t elemSize) { + ElemType* destElem = static_cast<ElemType*>(dest); + ElemType* srcElem = static_cast<ElemType*>(src); + ElemType* destElemEnd = destElem + count; +#ifdef DEBUG + ElemType* srcElemEnd = srcElem + count; + MOZ_ASSERT(srcElemEnd <= destElem || srcElemEnd > destElemEnd); +#endif + while (destElem != destElemEnd) { + traits::Construct(destElem, *srcElem); + traits::Destruct(srcElem); + ++destElem; + ++srcElem; + } + } + + static void CopyHeaderAndElements(void* dest, void* src, size_t count, size_t elemSize) { + nsTArrayHeader* destHeader = static_cast<nsTArrayHeader*>(dest); + nsTArrayHeader* srcHeader = static_cast<nsTArrayHeader*>(src); + *destHeader = *srcHeader; + CopyElements(static_cast<uint8_t*>(dest) + sizeof(nsTArrayHeader), + static_cast<uint8_t*>(src) + sizeof(nsTArrayHeader), + count, elemSize); + } + + static void MoveElements(void* dest, void* src, size_t count, size_t elemSize) { + ElemType* destElem = static_cast<ElemType*>(dest); + ElemType* srcElem = static_cast<ElemType*>(src); + ElemType* destElemEnd = destElem + count; + ElemType* srcElemEnd = srcElem + count; + if (destElem == srcElem) { + return; // In practice, we don't do this. + } else if (srcElemEnd > destElem && srcElemEnd < destElemEnd) { + while (destElemEnd != destElem) { + --destElemEnd; + --srcElemEnd; + traits::Construct(destElemEnd, *srcElemEnd); + traits::Destruct(srcElem); + } + } else { + CopyElements(dest, src, count, elemSize); + } + } +}; + +// +// The default behaviour is to use memcpy/memmove for everything. +// +template <class E> +struct nsTArray_CopyElements : public nsTArray_CopyWithMemutils {}; + +// +// JS::Heap<E> elements require constructors/destructors to be called and so is +// specialized here. +// +template <class E> +struct nsTArray_CopyElements<JS::Heap<E> > : public nsTArray_CopyWithConstructors<E> {}; + +// +// Base class for nsTArray_Impl that is templated on element type and derived +// nsTArray_Impl class, to allow extra conversions to be added for specific +// types. +// +template <class E, class Derived> +struct nsTArray_TypedBase : public nsTArray_SafeElementAtHelper<E, Derived> {}; + +// +// Specialization of nsTArray_TypedBase for arrays containing JS::Heap<E> +// elements. +// +// These conversions are safe because JS::Heap<E> and E share the same +// representation, and since the result of the conversions are const references +// we won't miss any barriers. +// +// The static_cast is necessary to obtain the correct address for the derived +// class since we are a base class used in multiple inheritance. +// +template <class E, class Derived> +struct nsTArray_TypedBase<JS::Heap<E>, Derived> + : public nsTArray_SafeElementAtHelper<JS::Heap<E>, Derived> +{ + operator const nsTArray<E>& () { + MOZ_STATIC_ASSERT(sizeof(E) == sizeof(JS::Heap<E>), + "JS::Heap<E> must be binary compatible with E."); + Derived* self = static_cast<Derived*>(this); + return *reinterpret_cast<nsTArray<E> *>(self); + } + + operator const FallibleTArray<E>& () { + Derived* self = static_cast<Derived*>(this); + return *reinterpret_cast<FallibleTArray<E> *>(self); + } +}; + + +// // nsTArray_Impl contains most of the guts supporting nsTArray, FallibleTArray, // nsAutoTArray, and AutoFallibleTArray. // // The only situation in which you might need to use nsTArray_Impl in your code // is if you're writing code which mutates a TArray which may or may not be // infallible. // // Code which merely reads from a TArray which may or may not be infallible can // simply cast the TArray to |const nsTArray&|; both fallible and infallible // TArrays can be cast to |const nsTArray&|. // template<class E, class Alloc> -class nsTArray_Impl : public nsTArray_base<Alloc>, - public nsTArray_SafeElementAtHelper<E, nsTArray_Impl<E, Alloc> > +class nsTArray_Impl : public nsTArray_base<Alloc, nsTArray_CopyElements<E> >, + public nsTArray_TypedBase<E, nsTArray_Impl<E, Alloc> > { public: - typedef nsTArray_base<Alloc> base_type; - typedef typename base_type::size_type size_type; - typedef typename base_type::index_type index_type; - typedef E elem_type; - typedef nsTArray_Impl<E, Alloc> self_type; - typedef nsTArrayElementTraits<E> elem_traits; + typedef nsTArray_CopyElements<E> copy_type; + typedef nsTArray_base<Alloc, copy_type> base_type; + typedef typename base_type::size_type size_type; + typedef typename base_type::index_type index_type; + typedef E elem_type; + typedef nsTArray_Impl<E, Alloc> self_type; + typedef nsTArrayElementTraits<E> elem_traits; typedef nsTArray_SafeElementAtHelper<E, self_type> safeelementat_helper_type; using safeelementat_helper_type::SafeElementAt; using base_type::EmptyHdr; // A special value that is used to indicate an invalid or unknown index // into the array. enum { @@ -711,17 +853,17 @@ public: } // This method provides direct, readonly access to the array elements. // @return A pointer to the first element of the array. If the array is // empty, then this pointer must not be dereferenced. const elem_type* Elements() const { return reinterpret_cast<const elem_type *>(Hdr() + 1); } - + // This method provides direct access to the i'th element of the array. // The given index must be within the array bounds. // @param i The index of an element in the array. // @return A reference to the i'th element of the array. elem_type& ElementAt(index_type i) { MOZ_ASSERT(i < Length(), "invalid array index"); return Elements()[i]; } @@ -1078,28 +1220,28 @@ public: // Append a new element without copy-constructing. This is useful to avoid // temporaries. // @return A pointer to the newly appended element, or null on OOM. elem_type *AppendElement() { return AppendElements(1); } - // Move all elements from another array to the end of this array without + // Move all elements from another array to the end of this array without // calling copy constructors or destructors. // @return A pointer to the newly appended elements, or null on OOM. template<class Item, class Allocator> elem_type *MoveElementsFrom(nsTArray_Impl<Item, Allocator>& array) { MOZ_ASSERT(&array != this, "argument must be different array"); index_type len = Length(); index_type otherLen = array.Length(); if (!Alloc::Successful(this->EnsureCapacity(len + otherLen, sizeof(elem_type)))) return nullptr; - memcpy(Elements() + len, array.Elements(), otherLen * sizeof(elem_type)); - this->IncrementLength(otherLen); + copy_type::CopyElements(Elements() + len, array.Elements(), otherLen, sizeof(elem_type)); + this->IncrementLength(otherLen); array.ShiftData(0, otherLen, 0, sizeof(elem_type), MOZ_ALIGNOF(elem_type)); return Elements() + len; } // This method removes a range of elements from this array. // @param start The starting index of the elements to remove. // @param count The number of elements to remove. void RemoveElementsAt(index_type start, size_type count) { @@ -1196,17 +1338,17 @@ public: // @return True if the operation succeeded; false otherwise. // See also TruncateLength if the new length is guaranteed to be // smaller than the old. bool SetLength(size_type newLen) { size_type oldLen = Length(); if (newLen > oldLen) { return InsertElementsAt(oldLen, newLen - oldLen) != nullptr; } - + TruncateLength(newLen); return true; } // This method modifies the length of the array, but may only be // called when the new length is shorter than the old. It can // therefore be called when elem_type has no default constructor, // unlike SetLength. It removes elements from the array (see also @@ -1278,17 +1420,17 @@ typename Alloc::ResultType EnsureLengthA