diff --git a/accessible/ipc/DocAccessibleParent.cpp b/accessible/ipc/DocAccessibleParent.cpp index 92ee44fef646..990b5e75fe8e 100644 --- a/accessible/ipc/DocAccessibleParent.cpp +++ b/accessible/ipc/DocAccessibleParent.cpp @@ -403,9 +403,10 @@ DocAccessibleParent::RecvShutdown() { Destroy(); - if (!static_cast(Manager())->IsDestroyed()) { + auto mgr = static_cast(Manager()); + if (!mgr->IsDestroyed()) { if (!PDocAccessibleParent::Send__delete__(this)) { - return IPC_FAIL_NO_REASON(this); + return IPC_FAIL_NO_REASON(mgr); } } diff --git a/b2g/chrome/content/devtools/hud.js b/b2g/chrome/content/devtools/hud.js index 08e7906b31aa..78e8afb968a1 100644 --- a/b2g/chrome/content/devtools/hud.js +++ b/b2g/chrome/content/devtools/hud.js @@ -317,62 +317,7 @@ Target.prototype = { }, _logHistogram(metric) { - if (!developerHUD._telemetry || metric.skipTelemetry) { - return; - } - - metric.appName = this.appName; - if (!metric.appName) { - return; - } - - let metricName = metric.name.toUpperCase(); - let metricAppName = metric.appName.toUpperCase(); - if (!metric.custom) { - let keyedMetricName = 'DEVTOOLS_HUD_' + metricName; - try { - let keyed = Services.telemetry.getKeyedHistogramById(keyedMetricName); - if (keyed) { - keyed.add(metric.appName, parseInt(metric.value, 10)); - developerHUD._histograms.add(keyedMetricName); - telemetryDebug(keyedMetricName, metric.value, metric.appName); - } - } catch(err) { - console.error('Histogram error is metricname added to histograms.json:' - + keyedMetricName); - } - } else { - let histogramName = CUSTOM_HISTOGRAM_PREFIX + metricAppName + '_' - + metricName; - // This is a call to add a value to an existing histogram. - if (typeof metric.value !== 'undefined') { - Services.telemetry.getAddonHistogram(metricAppName, - CUSTOM_HISTOGRAM_PREFIX + metricName).add(parseInt(metric.value, 10)); - telemetryDebug(histogramName, metric.value); - return; - } - - // The histogram already exists and are not adding data to it. - if (developerHUD._customHistograms.has(histogramName)) { - return; - } - - // This is a call to create a new histogram. - try { - let metricType = parseInt(metric.type, 10); - if (metricType === Services.telemetry.HISTOGRAM_COUNT) { - Services.telemetry.registerAddonHistogram(metricAppName, - CUSTOM_HISTOGRAM_PREFIX + metricName, metricType); - } else { - Services.telemetry.registerAddonHistogram(metricAppName, - CUSTOM_HISTOGRAM_PREFIX + metricName, metricType, metric.min, - metric.max, metric.buckets); - } - developerHUD._customHistograms.add(histogramName); - } catch (err) { - console.error('Histogram error: ' + err); - } - } + //method left as no-op as histograms are not in use anymore. } }; diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 84dbbc36774c..1a40b97a762d 100755 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -1147,7 +1147,7 @@ var gBrowserInit = { // we are swapping in. gBrowser.updateBrowserRemoteness(gBrowser.selectedBrowser, tabToOpen.linkedBrowser.isRemoteBrowser, - tabToOpen.linkedBrowser.remoteType); + { remoteType: tabToOpen.linkedBrowser.remoteType }); gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, tabToOpen); } catch (e) { Cu.reportError(e); @@ -4254,13 +4254,13 @@ var XULBrowserWindow = { forceInitialBrowserRemote: function(aRemoteType) { let initBrowser = document.getAnonymousElementByAttribute(gBrowser, "anonid", "initialBrowser"); - gBrowser.updateBrowserRemoteness(initBrowser, true, aRemoteType, null); + gBrowser.updateBrowserRemoteness(initBrowser, true, { remoteType: aRemoteType }); }, forceInitialBrowserNonRemote: function(aOpener) { let initBrowser = document.getAnonymousElementByAttribute(gBrowser, "anonid", "initialBrowser"); - gBrowser.updateBrowserRemoteness(initBrowser, false, E10SUtils.NOT_REMOTE, aOpener); + gBrowser.updateBrowserRemoteness(initBrowser, false, { opener: aOpener }); }, setDefaultStatus: function(status) { @@ -4669,6 +4669,17 @@ var XULBrowserWindow = { if (loadingDone) return; this.onStatusChange(gBrowser.webProgress, null, 0, aMessage); + }, + + navigateAndRestoreByIndex: function XWB_navigateAndRestoreByIndex(aBrowser, aIndex) { + let tab = gBrowser.getTabForBrowser(aBrowser); + if (tab) { + SessionStore.navigateAndRestore(tab, {}, aIndex); + return; + } + + throw new Error("Trying to navigateAndRestore a browser which was " + + "not attached to this tabbrowser is unsupported"); } }; diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index c72afcecb542..cdbce306900d 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -1669,38 +1669,37 @@ - - - + - + - + diff --git a/browser/base/content/test/general/browser.ini b/browser/base/content/test/general/browser.ini index 058b1f3292cd..93072971e05e 100644 --- a/browser/base/content/test/general/browser.ini +++ b/browser/base/content/test/general/browser.ini @@ -492,3 +492,5 @@ tags = mcb [browser_newwindow_focus.js] skip-if = (os == "linux" && !e10s) # Bug 1263254 - Perma fails on Linux without e10s for some reason. [browser_bug1299667.js] +[browser_close_dependent_tabs.js] +skip-if = !e10s # GroupedSHistory is e10s-only diff --git a/browser/base/content/test/general/browser_close_dependent_tabs.js b/browser/base/content/test/general/browser_close_dependent_tabs.js new file mode 100644 index 000000000000..0c3f00e242d2 --- /dev/null +++ b/browser/base/content/test/general/browser_close_dependent_tabs.js @@ -0,0 +1,51 @@ +add_task(function* () { + yield SpecialPowers.pushPrefEnv({ + set: [["browser.groupedhistory.enabled", true]] + }); + + // Wait for a process change and then fulfil the promise. + function awaitProcessChange(browser) { + return new Promise(resolve => { + browser.addEventListener("BrowserChangedProcess", function bcp(e) { + browser.removeEventListener("BrowserChangedProcess", bcp); + ok(true, "The browser changed process!"); + resolve(); + }); + }); + } + + let tab2; + + // Test 1: Create prerendered browser, and don't switch to it, then close the tab + yield BrowserTestUtils.withNewTab({ gBrowser, url: "data:text/html,a" }, function* (browser1) { + // Set up the grouped SHEntry setup + tab2 = gBrowser.loadOneTab("data:text/html,b", { + referrerPolicy: Ci.nsIHttpChannel.REFERRER_POLICY_DEFAULT, + allowThirdPartyFixup: true, + relatedToCurrent: true, + isPrerendered: true, + }); + }); + + // At this point tab2 should be closed + todo(!tab2.linkedBrowser, "The new tab should be closed"); + yield BrowserTestUtils.removeTab(tab2); // XXX: Shouldn't be needed once the todo is fixed + + // Test 2: Create prerendered browser, switch to it, then close the tab + yield BrowserTestUtils.withNewTab({ gBrowser, url: "data:text/html,a" }, function* (browser1) { + // Set up the grouped SHEntry setup + tab2 = gBrowser.loadOneTab("data:text/html,b", { + referrerPolicy: Ci.nsIHttpChannel.REFERRER_POLICY_DEFAULT, + allowThirdPartyFixup: true, + relatedToCurrent: true, + isPrerendered: true, + }); + yield BrowserTestUtils.browserLoaded(tab2.linkedBrowser); + browser1.frameLoader.appendPartialSessionHistoryAndSwap( + tab2.linkedBrowser.frameLoader); + yield awaitProcessChange(browser1); + }); + + // At this point tab2 should be closed + ok(!tab2.linkedBrowser, "The new tab should be closed"); +}); diff --git a/browser/components/sessionstore/SessionStore.jsm b/browser/components/sessionstore/SessionStore.jsm index c78bd91a32ef..9953a57de651 100644 --- a/browser/components/sessionstore/SessionStore.jsm +++ b/browser/components/sessionstore/SessionStore.jsm @@ -3611,8 +3611,17 @@ var SessionStoreInternal = { // flip the remoteness of any browser that is not being displayed. this.markTabAsRestoring(aTab); + // We need a new frameloader either if we are reloading into a fresh + // process, or we have a browser with a grouped session history (as we don't + // support restoring into browsers with grouped session histories directly). + let newFrameloader = + aReloadInFreshProcess || !!browser.frameLoader.groupedSessionHistory; let isRemotenessUpdate = - tabbrowser.updateBrowserRemotenessByURL(browser, uri, aReloadInFreshProcess); + tabbrowser.updateBrowserRemotenessByURL(browser, uri, { + freshProcess: aReloadInFreshProcess, + newFrameloader: newFrameloader, + }); + if (isRemotenessUpdate) { // We updated the remoteness, so we need to send the history down again. // diff --git a/browser/config/tooltool-manifests/win32/clang.manifest b/browser/config/tooltool-manifests/win32/clang.manifest index a6c3829fa8b4..16c85d192cbb 100644 --- a/browser/config/tooltool-manifests/win32/clang.manifest +++ b/browser/config/tooltool-manifests/win32/clang.manifest @@ -32,8 +32,8 @@ }, { "version": "clang 4.0pre/r286542", -"size": 206088429, -"digest": "a6de709a5097d98084b6111e340f53cd8397736f783aa6f76c9d45bcb694d3983ab3b7aea7f96756fcc4ee44f7b0e57df5ebabbd59242cd51942742fe1769d9a", +"size": 222604502, +"digest": "cea6119131adb66e0b7ec5030b00922ac95e4e97249fcab9561a848ea60b7f80536c9171a07136afcb79decbcdb20099a5e7ee493013710b8ba5ae072ad40851", "algorithm": "sha512", "filename": "clang.tar.bz2", "unpack": true diff --git a/browser/config/tooltool-manifests/win64/clang.manifest b/browser/config/tooltool-manifests/win64/clang.manifest index 23286e227cbd..5501f9c9bc93 100644 --- a/browser/config/tooltool-manifests/win64/clang.manifest +++ b/browser/config/tooltool-manifests/win64/clang.manifest @@ -33,8 +33,8 @@ }, { "version": "clang 4.0pre/r286542", -"size": 210049255, -"digest": "8da8c653ea1948c13aa152cf0b1b0fa94e9619c49006ec80cc625d33cbf2dafa3533764f38fc0213e469cc786a738e5099f0e39d52e30f9a711718e8a3124311", +"size": 226755339, +"digest": "3c598607c36e70788ca7dbdf0d835f9e44fbcaa7b1ed77ef9971d743a5a230bebc0ccd2bcdf97f63ed4546d1b83f4c3556f35c30589c755aaaefbd674f750e22", "algorithm": "sha512", "filename": "clang.tar.bz2", "unpack": true diff --git a/build/build-clang/build-clang.py b/build/build-clang/build-clang.py index 04f4e5524483..f69a6a1ab92f 100755 --- a/build/build-clang/build-clang.py +++ b/build/build-clang/build-clang.py @@ -137,6 +137,11 @@ def install_libgcc(gcc_dir, clang_dir): copy_dir_contents(include_dir, clang_include_dir) +def install_import_library(build_dir, clang_dir): + shutil.copy2(os.path.join(build_dir, "lib", "clang.lib"), + os.path.join(clang_dir, "lib")) + + def svn_co(source_dir, url, directory, revision): run_in(source_dir, ["svn", "co", "-q", "-r", revision, url, directory]) @@ -211,7 +216,10 @@ def build_one_stage(cc, cxx, src_dir, stage_dir, build_libcxx, if is_linux(): install_libgcc(gcc_dir, inst_dir) - + # For some reasons the import library clang.lib of clang.exe is not + # installed, so we copy it by ourselves. + if is_windows(): + install_import_library(build_dir, inst_dir) def get_compiler(config, key): if key not in config: diff --git a/docshell/shistory/nsIGroupedSHistory.idl b/docshell/shistory/nsIGroupedSHistory.idl index 4001dcc34b4b..7c3f6e5dc829 100644 --- a/docshell/shistory/nsIGroupedSHistory.idl +++ b/docshell/shistory/nsIGroupedSHistory.idl @@ -39,9 +39,19 @@ interface nsIGroupedSHistory : nsISupports * corresponding to the given global index. Note it doesn't swap frameloaders, * but rather return the target loader for the caller to swap. * - * @param aGlobalIndex The global index to navigate to. - * @param aTargetLoaderToSwap The owner frameloader of the to-be-navigate - * partial session history. + * This function may throw NS_ERROR_NOT_AVAILABLE if the frameloader to swap + * to is dead. + * + * @param aGlobalIndex + * The global index to navigate to. + * @return The frameloader which needs to be swapped in, or null if no + * frameloader needs to be swapped. */ - void gotoIndex(in unsigned long aGlobalIndex, out nsIFrameLoader aTargetLoaderToSwap); + nsIFrameLoader gotoIndex(in unsigned long aGlobalIndex); + + /** + * Close the FrameLoaderOwners of the inactive PartialSHistories in this GlobalSHistory. + * This does not remove the PartialSHistories from the GroupedSHistory. + */ + void closeInactiveFrameLoaderOwners(); }; diff --git a/docshell/test/browser/browser.ini b/docshell/test/browser/browser.ini index 9211092a49c8..f362d88c7d94 100644 --- a/docshell/test/browser/browser.ini +++ b/docshell/test/browser/browser.ini @@ -91,3 +91,5 @@ skip-if = true # Bug 1220415 [browser_timelineMarkers-04.js] [browser_timelineMarkers-05.js] [browser_ua_emulation.js] +[browser_grouped_shistory_dead_navigate.js] +skip-if = !e10s diff --git a/docshell/test/browser/browser_grouped_shistory_dead_navigate.js b/docshell/test/browser/browser_grouped_shistory_dead_navigate.js new file mode 100644 index 000000000000..2e898a101681 --- /dev/null +++ b/docshell/test/browser/browser_grouped_shistory_dead_navigate.js @@ -0,0 +1,44 @@ +add_task(function* () { + yield SpecialPowers.pushPrefEnv({ + set: [["browser.groupedhistory.enabled", true]] + }); + + // Wait for a process change and then fulfil the promise. + function awaitProcessChange(browser) { + return new Promise(resolve => { + browser.addEventListener("BrowserChangedProcess", function bcp(e) { + browser.removeEventListener("BrowserChangedProcess", bcp); + ok(true, "The browser changed process!"); + resolve(); + }); + }); + } + + yield BrowserTestUtils.withNewTab({ gBrowser, url: "data:text/html,a" }, function* (browser1) { + // Set up the grouped SHEntry setup + let tab2 = gBrowser.loadOneTab("data:text/html,b", { + referrerPolicy: Ci.nsIHttpChannel.REFERRER_POLICY_DEFAULT, + allowThirdPartyFixup: true, + relatedToCurrent: true, + isPrerendered: true, + }); + yield BrowserTestUtils.browserLoaded(tab2.linkedBrowser); + browser1.frameLoader.appendPartialSessionHistoryAndSwap( + tab2.linkedBrowser.frameLoader); + yield awaitProcessChange(browser1); + + // Close tab2 such that the back frameloader is dead + yield BrowserTestUtils.removeTab(tab2); + browser1.goBack(); + yield BrowserTestUtils.browserLoaded(browser1); + yield ContentTask.spawn(browser1, null, function() { + is(content.window.location + "", "data:text/html,a"); + let webNav = content.window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation); + is(webNav.canGoForward, true, "canGoForward is correct"); + is(webNav.canGoBack, false, "canGoBack is correct"); + }); + is(browser1.frameLoader.groupedSessionHistory, null, + "browser1's session history is now complete"); + }); +}); diff --git a/dom/animation/test/mozilla/file_discrete-animations.html b/dom/animation/test/mozilla/file_discrete-animations.html index a79947650a5e..8820365a727c 100644 --- a/dom/animation/test/mozilla/file_discrete-animations.html +++ b/dom/animation/test/mozilla/file_discrete-animations.html @@ -75,11 +75,6 @@ const gMozillaSpecificProperties = { from: "ignore", to: "stretch-to-fit" }, - "-moz-tab-size": { - // https://drafts.csswg.org/css-text-3/#propdef-tab-size - from: "1", - to: "5" - }, "-moz-text-size-adjust": { // https://drafts.csswg.org/css-size-adjust/#propdef-text-size-adjust from: "none", diff --git a/dom/base/GroupedSHistory.cpp b/dom/base/GroupedSHistory.cpp index 266b1fd36d46..92ddef9993f1 100644 --- a/dom/base/GroupedSHistory.cpp +++ b/dom/base/GroupedSHistory.cpp @@ -99,6 +99,9 @@ NS_IMETHODIMP GroupedSHistory::GotoIndex(uint32_t aGlobalIndex, nsIFrameLoader** aTargetLoaderToSwap) { + MOZ_ASSERT(aTargetLoaderToSwap); + *aTargetLoaderToSwap = nullptr; + nsCOMPtr currentPartialHistory = mPartialHistories[mIndexOfActivePartialHistory]; if (!currentPartialHistory) { @@ -118,7 +121,15 @@ GroupedSHistory::GotoIndex(uint32_t aGlobalIndex, uint32_t count = partialHistory->GetCount(); if (offset <= aGlobalIndex && (offset + count) > aGlobalIndex) { uint32_t targetIndex = aGlobalIndex - offset; - partialHistory->GetOwnerFrameLoader(aTargetLoaderToSwap); + + // Check if we are trying to swap to a dead frameloader, and return + // NS_ERROR_NOT_AVAILABLE if we are. + nsCOMPtr frameLoader; + partialHistory->GetOwnerFrameLoader(getter_AddRefs(frameLoader)); + if (!frameLoader || frameLoader->GetIsDead()) { + return NS_ERROR_NOT_AVAILABLE; + } + if ((size_t)mIndexOfActivePartialHistory == i) { return NS_OK; } @@ -127,6 +138,9 @@ GroupedSHistory::GotoIndex(uint32_t aGlobalIndex, NS_FAILED(partialHistory->OnActive(mCount, targetIndex))) { return NS_ERROR_FAILURE; } + + // Return the target frameloader to the caller. + frameLoader.forget(aTargetLoaderToSwap); return NS_OK; } } @@ -168,5 +182,18 @@ GroupedSHistory::GroupedHistoryEnabled() { return Preferences::GetBool("browser.groupedhistory.enabled", false); } +NS_IMETHODIMP +GroupedSHistory::CloseInactiveFrameLoaderOwners() +{ + for (int32_t i = 0; i < mPartialHistories.Count(); ++i) { + if (i != mIndexOfActivePartialHistory) { + nsCOMPtr loader; + mPartialHistories[i]->GetOwnerFrameLoader(getter_AddRefs(loader)); + loader->RequestFrameLoaderClose(); + } + } + return NS_OK; +} + } // namespace dom } // namespace mozilla diff --git a/dom/base/TimeoutManager.cpp b/dom/base/TimeoutManager.cpp index 72e0d0c0510d..9ea6b68fb819 100644 --- a/dom/base/TimeoutManager.cpp +++ b/dom/base/TimeoutManager.cpp @@ -76,7 +76,6 @@ CalculateNewBackPressureDelayMS(uint32_t aBacklogDepth) TimeoutManager::TimeoutManager(nsGlobalWindow& aWindow) : mWindow(aWindow), - mTimeoutInsertionPoint(nullptr), mTimeoutIdCounter(1), mTimeoutFiringDepth(0), mRunningTimeout(nullptr), @@ -226,7 +225,7 @@ TimeoutManager::ClearTimeout(int32_t aTimerId, Timeout::Reason aReason) uint32_t timerId = (uint32_t)aTimerId; Timeout* timeout; - for (timeout = mTimeouts.getFirst(); timeout; timeout = timeout->getNext()) { + for (timeout = mTimeouts.GetFirst(); timeout; timeout = timeout->getNext()) { if (timeout->mTimeoutId == timerId && timeout->mReason == aReason) { if (timeout->mRunning) { /* We're running from inside the timeout. Mark this @@ -296,7 +295,7 @@ TimeoutManager::RunTimeout(Timeout* aTimeout) // whose mWhen is greater than deadline, since once that happens we know // nothing past that point is expired. last_expired_timeout = nullptr; - for (Timeout* timeout = mTimeouts.getFirst(); + for (Timeout* timeout = mTimeouts.GetFirst(); timeout && timeout->mWhen <= deadline; timeout = timeout->getNext()) { if (timeout->mFiringDepth == 0) { @@ -339,12 +338,12 @@ TimeoutManager::RunTimeout(Timeout* aTimeout) last_expired_timeout->setNext(dummy_timeout); RefPtr timeoutExtraRef(dummy_timeout); - last_insertion_point = mTimeoutInsertionPoint; - // If we ever start setting mTimeoutInsertionPoint to a non-dummy timeout, - // the logic in ResetTimersForThrottleReduction will need to change. - mTimeoutInsertionPoint = dummy_timeout; + last_insertion_point = mTimeouts.InsertionPoint(); + // If we ever start setting insertion point to a non-dummy timeout, the logic + // in ResetTimersForThrottleReduction will need to change. + mTimeouts.SetInsertionPoint(dummy_timeout); - for (Timeout* timeout = mTimeouts.getFirst(); + for (Timeout* timeout = mTimeouts.GetFirst(); timeout != dummy_timeout && !mWindow.IsFrozen(); timeout = nextTimeout) { nextTimeout = timeout->getNext(); @@ -387,7 +386,7 @@ TimeoutManager::RunTimeout(Timeout* aTimeout) MOZ_ASSERT(dummy_timeout->HasRefCntOne(), "dummy_timeout may leak"); Unused << timeoutExtraRef.forget().take(); - mTimeoutInsertionPoint = last_insertion_point; + mTimeouts.SetInsertionPoint(last_insertion_point); return; } @@ -417,7 +416,7 @@ TimeoutManager::RunTimeout(Timeout* aTimeout) timeoutExtraRef = nullptr; MOZ_ASSERT(dummy_timeout->HasRefCntOne(), "dummy_timeout may leak"); - mTimeoutInsertionPoint = last_insertion_point; + mTimeouts.SetInsertionPoint(last_insertion_point); MaybeApplyBackPressure(); } @@ -598,15 +597,15 @@ TimeoutManager::ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS TimeStamp now = TimeStamp::Now(); - // If mTimeoutInsertionPoint is non-null, we're in the middle of firing - // timers and the timers we're planning to fire all come before - // mTimeoutInsertionPoint; mTimeoutInsertionPoint itself is a dummy timeout - // with an mWhen that may be semi-bogus. In that case, we don't need to do - // anything with mTimeoutInsertionPoint or anything before it, so should - // start at the timer after mTimeoutInsertionPoint, if there is one. + // If insertion point is non-null, we're in the middle of firing timers and + // the timers we're planning to fire all come before insertion point; + // insertion point itself is a dummy timeout with an mWhen that may be + // semi-bogus. In that case, we don't need to do anything with insertion + // point or anything before it, so should start at the timer after insertion + // point, if there is one. // Otherwise, start at the beginning of the list. - for (Timeout* timeout = mTimeoutInsertionPoint ? - mTimeoutInsertionPoint->getNext() : mTimeouts.getFirst(); + for (Timeout* timeout = mTimeouts.InsertionPoint() ? + mTimeouts.InsertionPoint()->getNext() : mTimeouts.GetFirst(); timeout; ) { // It's important that this check be <= so that we guarantee that // taking std::max with |now| won't make a quantity equal to @@ -687,14 +686,15 @@ TimeoutManager::ClearAllTimeouts() Timeout* timeout; Timeout* nextTimeout; - for (timeout = mTimeouts.getFirst(); timeout; timeout = nextTimeout) { + for (timeout = mTimeouts.GetFirst(); timeout; timeout = nextTimeout) { /* If RunTimeout() is higher up on the stack for this window, e.g. as a result of document.write from a timeout, then we need to reset the list insertion point for newly-created timeouts in case the user adds a timeout, before we pop the stack back to RunTimeout. */ - if (mRunningTimeout == timeout) - mTimeoutInsertionPoint = nullptr; + if (mRunningTimeout == timeout) { + mTimeouts.SetInsertionPoint(nullptr); + } nextTimeout = timeout->getNext(); @@ -716,18 +716,17 @@ TimeoutManager::ClearAllTimeouts() } // Clear out our list - mTimeouts.clear(); + mTimeouts.Clear(); } void TimeoutManager::InsertTimeoutIntoList(Timeout* aTimeout) { - // Start at mLastTimeout and go backwards. Don't go further than - // mTimeoutInsertionPoint, though. This optimizes for the common case of - // insertion at the end. + // Start at mLastTimeout and go backwards. Don't go further than insertion + // point, though. This optimizes for the common case of insertion at the end. Timeout* prevSibling; - for (prevSibling = mTimeouts.getLast(); - prevSibling && prevSibling != mTimeoutInsertionPoint && + for (prevSibling = mTimeouts.GetLast(); + prevSibling && prevSibling != mTimeouts.InsertionPoint() && // This condition needs to match the one in SetTimeoutOrInterval that // determines whether to set mWhen or mTimeRemaining. (mWindow.IsFrozen() ? @@ -741,7 +740,7 @@ TimeoutManager::InsertTimeoutIntoList(Timeout* aTimeout) if (prevSibling) { prevSibling->setNext(aTimeout); } else { - mTimeouts.insertFront(aTimeout); + mTimeouts.InsertFront(aTimeout); } aTimeout->mFiringDepth = 0; @@ -775,7 +774,7 @@ TimeoutManager::EndRunningTimeout(Timeout* aTimeout) void TimeoutManager::UnmarkGrayTimers() { - for (Timeout* timeout = mTimeouts.getFirst(); + for (Timeout* timeout = mTimeouts.GetFirst(); timeout; timeout = timeout->getNext()) { if (timeout->mScriptHandler) { @@ -787,7 +786,7 @@ TimeoutManager::UnmarkGrayTimers() void TimeoutManager::Suspend() { - for (Timeout* t = mTimeouts.getFirst(); t; t = t->getNext()) { + for (Timeout* t = mTimeouts.GetFirst(); t; t = t->getNext()) { // Leave the timers with the current time remaining. This will // cause the timers to potentially fire when the window is // Resume()'d. Time effectively passes while suspended. @@ -810,7 +809,7 @@ TimeoutManager::Resume() TimeStamp now = TimeStamp::Now(); DebugOnly _seenDummyTimeout = false; - for (Timeout* t = mTimeouts.getFirst(); t; t = t->getNext()) { + for (Timeout* t = mTimeouts.GetFirst(); t; t = t->getNext()) { // There's a chance we're being called with RunTimeout on the stack in which // case we have a dummy timeout in the list that *must not* be resumed. It // can be identified by a null mWindow. @@ -856,7 +855,7 @@ void TimeoutManager::Freeze() { TimeStamp now = TimeStamp::Now(); - for (Timeout *t = mTimeouts.getFirst(); t; t = t->getNext()) { + for (Timeout *t = mTimeouts.GetFirst(); t; t = t->getNext()) { // Save the current remaining time for this timeout. We will // re-apply it when the window is Thaw()'d. This effectively // shifts timers to the right as if time does not pass while @@ -879,7 +878,7 @@ TimeoutManager::Thaw() TimeStamp now = TimeStamp::Now(); DebugOnly _seenDummyTimeout = false; - for (Timeout *t = mTimeouts.getFirst(); t; t = t->getNext()) { + for (Timeout *t = mTimeouts.GetFirst(); t; t = t->getNext()) { // There's a chance we're being called with RunTimeout on the stack in which // case we have a dummy timeout in the list that *must not* be resumed. It // can be identified by a null mWindow. diff --git a/dom/base/TimeoutManager.h b/dom/base/TimeoutManager.h index d090f037db9b..435a0c56e5d7 100644 --- a/dom/base/TimeoutManager.h +++ b/dom/base/TimeoutManager.h @@ -28,7 +28,7 @@ public: static uint32_t GetNestingLevel() { return sNestingLevel; } static void SetNestingLevel(uint32_t aLevel) { sNestingLevel = aLevel; } - bool HasTimeouts() const { return !mTimeouts.isEmpty(); } + bool HasTimeouts() const { return !mTimeouts.IsEmpty(); } nsresult SetTimeout(nsITimeoutHandler* aHandler, int32_t interval, bool aIsInterval, @@ -87,7 +87,7 @@ public: template void ForEachTimeout(Callable c) { - for (Timeout* timeout = mTimeouts.getFirst(); + for (Timeout* timeout = mTimeouts.GetFirst(); timeout; timeout = timeout->getNext()) { c(timeout); @@ -98,18 +98,46 @@ private: nsresult ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS); private: + typedef mozilla::LinkedList TimeoutList; + struct Timeouts { + Timeouts() + : mTimeoutInsertionPoint(nullptr) + { + } + + const Timeout* GetFirst() const { return mTimeoutList.getFirst(); } + Timeout* GetFirst() { return mTimeoutList.getFirst(); } + const Timeout* GetLast() const { return mTimeoutList.getLast(); } + Timeout* GetLast() { return mTimeoutList.getLast(); } + bool IsEmpty() const { return mTimeoutList.isEmpty(); } + void InsertFront(Timeout* aTimeout) { mTimeoutList.insertFront(aTimeout); } + void Clear() { mTimeoutList.clear(); } + + void SetInsertionPoint(Timeout* aTimeout) + { + mTimeoutInsertionPoint = aTimeout; + } + Timeout* InsertionPoint() + { + return mTimeoutInsertionPoint; + } + + private: + // mTimeoutList is generally sorted by mWhen, unless mTimeoutInsertionPoint is + // non-null. In that case, the dummy timeout pointed to by + // mTimeoutInsertionPoint may have a later mWhen than some of the timeouts + // that come after it. + TimeoutList mTimeoutList; + // If mTimeoutInsertionPoint is non-null, insertions should happen after it. + // This is a dummy timeout at the moment; if that ever changes, the logic in + // ResetTimersForThrottleReduction needs to change. + mozilla::dom::Timeout* mTimeoutInsertionPoint; + }; + // Each nsGlobalWindow object has a TimeoutManager member. This reference // points to that holder object. nsGlobalWindow& mWindow; - // mTimeouts is generally sorted by mWhen, unless mTimeoutInsertionPoint is - // non-null. In that case, the dummy timeout pointed to by - // mTimeoutInsertionPoint may have a later mWhen than some of the timeouts - // that come after it. - mozilla::LinkedList mTimeouts; - // If mTimeoutInsertionPoint is non-null, insertions should happen after it. - // This is a dummy timeout at the moment; if that ever changes, the logic in - // ResetTimersForThrottleReduction needs to change. - mozilla::dom::Timeout* mTimeoutInsertionPoint; + Timeouts mTimeouts; uint32_t mTimeoutIdCounter; uint32_t mTimeoutFiringDepth; mozilla::dom::Timeout* mRunningTimeout; diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index 9d3fb50bd4f1..78aee28b78fd 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -515,17 +515,70 @@ public: void ResolvedCallback(JSContext* aCx, JS::Handle aValue) override { + if (NS_WARN_IF(!mThis->mOwnerContent)) { + mPromise->MaybeRejectWithUndefined(); + return; + } + // Navigate the loader to the new index nsCOMPtr otherLoader; nsresult rv = mThis->mGroupedSessionHistory-> GotoIndex(mGlobalIndex, getter_AddRefs(otherLoader)); - if (NS_WARN_IF(!otherLoader || NS_FAILED(rv))) { + + // Check if the gotoIndex failed because the target frameloader is dead. We + // need to perform a navigateAndRestoreByIndex and then return to recover. + if (rv == NS_ERROR_NOT_AVAILABLE) { + // Get the nsIXULBrowserWindow so that we can call NavigateAndRestoreByIndex on it. + nsCOMPtr docShell = mThis->mOwnerContent->OwnerDoc()->GetDocShell(); + if (NS_WARN_IF(!docShell)) { + mPromise->MaybeRejectWithUndefined(); + return; + } + + nsCOMPtr treeOwner; + docShell->GetTreeOwner(getter_AddRefs(treeOwner)); + if (NS_WARN_IF(!treeOwner)) { + mPromise->MaybeRejectWithUndefined(); + return; + } + + nsCOMPtr window = do_GetInterface(treeOwner); + if (NS_WARN_IF(!window)) { + mPromise->MaybeRejectWithUndefined(); + return; + } + + nsCOMPtr xbw; + window->GetXULBrowserWindow(getter_AddRefs(xbw)); + if (NS_WARN_IF(!xbw)) { + mPromise->MaybeRejectWithUndefined(); + return; + } + + nsCOMPtr ourBrowser = do_QueryInterface(mThis->mOwnerContent); + if (NS_WARN_IF(!ourBrowser)) { + mPromise->MaybeRejectWithUndefined(); + return; + } + + rv = xbw->NavigateAndRestoreByIndex(ourBrowser, mGlobalIndex); + if (NS_WARN_IF(NS_FAILED(rv))) { + mPromise->MaybeRejectWithUndefined(); + return; + } + mPromise->MaybeResolveWithUndefined(); + return; + } + + // Check for any other type of failure + if (NS_WARN_IF(NS_FAILED(rv))) { mPromise->MaybeRejectWithUndefined(); return; } - nsFrameLoader* other = static_cast(otherLoader.get()); - if (other == mThis) { + // Perform the swap. + nsFrameLoader* other = static_cast(otherLoader.get()); + if (!other || other == mThis) { mPromise->MaybeRejectWithUndefined(); return; } @@ -3575,6 +3628,13 @@ nsFrameLoader::PopulateUserContextIdFromAttribute(DocShellOriginAttributes& aAtt return NS_OK; } +NS_IMETHODIMP +nsFrameLoader::GetIsDead(bool* aIsDead) +{ + *aIsDead = mDestroyCalled; + return NS_OK; +} + nsIMessageSender* nsFrameLoader::GetProcessMessageManager() const { diff --git a/dom/base/nsIFrameLoader.idl b/dom/base/nsIFrameLoader.idl index ed1bae0976c3..b530a9b825c0 100644 --- a/dom/base/nsIFrameLoader.idl +++ b/dom/base/nsIFrameLoader.idl @@ -258,6 +258,11 @@ interface nsIFrameLoader : nsISupports * across root docshells. */ readonly attribute nsIGroupedSHistory groupedSessionHistory; + + /** + * Is `true` if the frameloader is dead (destroy has been called on it) + */ + [infallible] readonly attribute boolean isDead; }; %{C++ diff --git a/dom/filehandle/ActorsParent.cpp b/dom/filehandle/ActorsParent.cpp index 2e727a03d479..4f4ff3a0d7eb 100644 --- a/dom/filehandle/ActorsParent.cpp +++ b/dom/filehandle/ActorsParent.cpp @@ -13,6 +13,7 @@ #include "mozilla/dom/PBackgroundFileHandleParent.h" #include "mozilla/dom/PBackgroundFileRequestParent.h" #include "mozilla/dom/indexedDB/ActorsParent.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseParent.h" #include "mozilla/dom/ipc/BlobParent.h" #include "nsAutoPtr.h" #include "nsComponentManagerUtils.h" @@ -1518,8 +1519,9 @@ BackgroundMutableFileParentBase::RecvDeleteMe() AssertIsOnBackgroundThread(); MOZ_ASSERT(!mActorDestroyed); + IProtocol* mgr = Manager(); if (!PBackgroundMutableFileParent::Send__delete__(this)) { - return IPC_FAIL_NO_REASON(this); + return IPC_FAIL_NO_REASON(mgr); } return IPC_OK(); } @@ -1843,8 +1845,9 @@ FileHandle::RecvDeleteMe() AssertIsOnBackgroundThread(); MOZ_ASSERT(!IsActorDestroyed()); + IProtocol* mgr = Manager(); if (!PBackgroundFileHandleParent::Send__delete__(this)) { - return IPC_FAIL_NO_REASON(this); + return IPC_FAIL_NO_REASON(mgr); } return IPC_OK(); } diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp index 2afdbe32b4b8..eed58f2d4dbe 100644 --- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -59,6 +59,7 @@ #include "mozilla/ipc/InputStreamParams.h" #include "mozilla/ipc/InputStreamUtils.h" #include "mozilla/ipc/PBackground.h" +#include "mozilla/ipc/PBackgroundParent.h" #include "mozilla/Scoped.h" #include "mozilla/storage/Variant.h" #include "nsAutoPtr.h" @@ -13727,8 +13728,9 @@ Factory::RecvDeleteMe() AssertIsOnBackgroundThread(); MOZ_ASSERT(!mActorDestroyed); + IProtocol* mgr = Manager(); if (!PBackgroundIDBFactoryParent::Send__delete__(this)) { - return IPC_FAIL_NO_REASON(this); + return IPC_FAIL_NO_REASON(mgr); } return IPC_OK(); } @@ -14666,8 +14668,9 @@ Database::RecvDeleteMe() AssertIsOnBackgroundThread(); MOZ_ASSERT(!mActorDestroyed); + IProtocol* mgr = Manager(); if (!PBackgroundIDBDatabaseParent::Send__delete__(this)) { - return IPC_FAIL_NO_REASON(this); + return IPC_FAIL_NO_REASON(mgr); } return IPC_OK(); } @@ -15791,8 +15794,9 @@ NormalTransaction::RecvDeleteMe() AssertIsOnBackgroundThread(); MOZ_ASSERT(!IsActorDestroyed()); + IProtocol* mgr = Manager(); if (!PBackgroundIDBTransactionParent::Send__delete__(this)) { - return IPC_FAIL_NO_REASON(this); + return IPC_FAIL_NO_REASON(mgr); } return IPC_OK(); } @@ -16097,8 +16101,9 @@ VersionChangeTransaction::RecvDeleteMe() AssertIsOnBackgroundThread(); MOZ_ASSERT(!IsActorDestroyed()); + IProtocol* mgr = Manager(); if (!PBackgroundIDBVersionChangeTransactionParent::Send__delete__(this)) { - return IPC_FAIL_NO_REASON(this); + return IPC_FAIL_NO_REASON(mgr); } return IPC_OK(); } @@ -16892,8 +16897,9 @@ Cursor::RecvDeleteMe() return IPC_FAIL_NO_REASON(this); } + IProtocol* mgr = Manager(); if (!PBackgroundIDBCursorParent::Send__delete__(this)) { - return IPC_FAIL_NO_REASON(this); + return IPC_FAIL_NO_REASON(mgr); } return IPC_OK(); } @@ -28927,8 +28933,9 @@ Utils::RecvDeleteMe() AssertIsOnBackgroundThread(); MOZ_ASSERT(!mActorDestroyed); + IProtocol* mgr = Manager(); if (!PBackgroundIndexedDBUtilsParent::Send__delete__(this)) { - return IPC_FAIL_NO_REASON(this); + return IPC_FAIL_NO_REASON(mgr); } return IPC_OK(); } diff --git a/dom/media/systemservices/CamerasParent.cpp b/dom/media/systemservices/CamerasParent.cpp index c2ffc85d3c47..c30ddaf12e0e 100644 --- a/dom/media/systemservices/CamerasParent.cpp +++ b/dom/media/systemservices/CamerasParent.cpp @@ -13,6 +13,7 @@ #include "mozilla/Services.h" #include "mozilla/Logging.h" #include "mozilla/ipc/BackgroundParent.h" +#include "mozilla/ipc/PBackgroundParent.h" #include "mozilla/Preferences.h" #include "nsIPermissionManager.h" #include "nsThreadUtils.h" @@ -1013,8 +1014,9 @@ CamerasParent::RecvAllDone() LOG((__PRETTY_FUNCTION__)); // Don't try to send anything to the child now mChildIsAlive = false; + IProtocol* mgr = Manager(); if (!Send__delete__(this)) { - return IPC_FAIL_NO_REASON(this); + return IPC_FAIL_NO_REASON(mgr); } return IPC_OK(); } diff --git a/dom/media/systemservices/MediaSystemResourceManagerParent.cpp b/dom/media/systemservices/MediaSystemResourceManagerParent.cpp index 667cb84661f0..f224fd9d8c3d 100644 --- a/dom/media/systemservices/MediaSystemResourceManagerParent.cpp +++ b/dom/media/systemservices/MediaSystemResourceManagerParent.cpp @@ -4,6 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/Unused.h" +#include "mozilla/layers/PImageBridgeParent.h" #include "MediaSystemResourceManagerParent.h" @@ -58,8 +59,9 @@ MediaSystemResourceManagerParent::RecvRelease(const uint32_t& aId) mozilla::ipc::IPCResult MediaSystemResourceManagerParent::RecvRemoveResourceManager() { + IProtocol* mgr = Manager(); if (!PMediaSystemResourceManagerParent::Send__delete__(this)) { - return IPC_FAIL_NO_REASON(this); + return IPC_FAIL_NO_REASON(mgr); } return IPC_OK(); } diff --git a/dom/plugins/ipc/BrowserStreamParent.cpp b/dom/plugins/ipc/BrowserStreamParent.cpp index 2947e229604c..ccf6a3e3f273 100644 --- a/dom/plugins/ipc/BrowserStreamParent.cpp +++ b/dom/plugins/ipc/BrowserStreamParent.cpp @@ -167,8 +167,9 @@ BrowserStreamParent::RecvStreamDestroyed() mStreamPeer = nullptr; mState = DELETING; + IProtocol* mgr = Manager(); if (!Send__delete__(this)) { - return IPC_FAIL_NO_REASON(this); + return IPC_FAIL_NO_REASON(mgr); } return IPC_OK(); } diff --git a/dom/workers/test/bug1063538_worker.js b/dom/workers/test/bug1063538_worker.js index a4faa8e43081..dc53dd2891aa 100644 --- a/dom/workers/test/bug1063538_worker.js +++ b/dom/workers/test/bug1063538_worker.js @@ -15,8 +15,8 @@ xhr.onloadend = function(e) { xhr.onprogress = function(e) { if (e.loaded > 0) { progressFired = true; + xhr.abort(); } - xhr.abort(); }; onmessage = function(e) { diff --git a/dom/workers/test/mochitest.ini b/dom/workers/test/mochitest.ini index 0d0e9d25b559..d4848819c3dc 100644 --- a/dom/workers/test/mochitest.ini +++ b/dom/workers/test/mochitest.ini @@ -107,6 +107,7 @@ support-files = script_createFile.js worker_suspended.js window_suspended.html + !/dom/base/test/file_bug945152.jar !/dom/base/test/file_websocket_basic_wsh.py !/dom/base/test/file_websocket_hello_wsh.py !/dom/base/test/file_websocket_http_resource.txt diff --git a/dom/xhr/XMLHttpRequestMainThread.cpp b/dom/xhr/XMLHttpRequestMainThread.cpp index 7a68d253dc2f..c9a7391b1bc7 100644 --- a/dom/xhr/XMLHttpRequestMainThread.cpp +++ b/dom/xhr/XMLHttpRequestMainThread.cpp @@ -673,7 +673,7 @@ XMLHttpRequestMainThread::CreatePartialBlob(ErrorResult& aRv) // Use progress info to determine whether load is complete, but use // mDataAvailable to ensure a slice is created based on the uncompressed // data count. - if (mLoadTotal == mLoadTransferred) { + if (mState == State::done) { mResponseBlob = mDOMBlob; } else { mResponseBlob = mDOMBlob->CreateSlice(0, mDataAvailable, @@ -688,7 +688,7 @@ XMLHttpRequestMainThread::CreatePartialBlob(ErrorResult& aRv) } nsAutoCString contentType; - if (mLoadTotal == mLoadTransferred) { + if (mState == State::done) { mChannel->GetContentType(contentType); } @@ -1790,7 +1790,15 @@ XMLHttpRequestMainThread::OnDataAvailable(nsIRequest *request, mDataAvailable += totalRead; - ChangeState(State::loading); + // Fire the first progress event/loading state change + if (mState != State::loading) { + ChangeState(State::loading); + if (!mFlagSynchronous) { + DispatchProgressEvent(this, ProgressEventType::progress, + mLoadTransferred, mLoadTotal); + } + mProgressSinceLastProgressEvent = false; + } if (!mFlagSynchronous && !mProgressTimerIsActive) { StartProgressEventTimer(); @@ -2098,10 +2106,6 @@ XMLHttpRequestMainThread::OnStopRequest(nsIRequest *request, nsISupports *ctxt, bool waitingForBlobCreation = false; if (NS_SUCCEEDED(status) && - (mResponseType == XMLHttpRequestResponseType::_empty || - mResponseType == XMLHttpRequestResponseType::Text)) { - mLoadTotal = mResponseBody.Length(); - } else if (NS_SUCCEEDED(status) && (mResponseType == XMLHttpRequestResponseType::Blob || mResponseType == XMLHttpRequestResponseType::Moz_blob)) { ErrorResult rv; @@ -2111,11 +2115,6 @@ XMLHttpRequestMainThread::OnStopRequest(nsIRequest *request, nsISupports *ctxt, if (mDOMBlob) { mResponseBlob = mDOMBlob; mDOMBlob = nullptr; - - mLoadTotal = mResponseBlob->GetSize(rv); - if (NS_WARN_IF(rv.Failed())) { - status = rv.StealNSResult(); - } } else { // Smaller files may be written in cache map instead of separate files. // Also, no-store response cannot be written in persistent cache. @@ -2126,8 +2125,7 @@ XMLHttpRequestMainThread::OnStopRequest(nsIRequest *request, nsISupports *ctxt, // mBlobStorage can be null if the channel is non-file non-cacheable // and if the response length is zero. MaybeCreateBlobStorage(); - mLoadTotal = - mBlobStorage->GetBlobWhenReady(GetOwner(), contentType, this); + mBlobStorage->GetBlobWhenReady(GetOwner(), contentType, this); waitingForBlobCreation = true; } else { // mBlobSet can be null if the channel is non-file non-cacheable @@ -2148,10 +2146,6 @@ XMLHttpRequestMainThread::OnStopRequest(nsIRequest *request, nsISupports *ctxt, } mResponseBlob = Blob::Create(GetOwner(), blobImpl); - mLoadTotal = mResponseBlob->GetSize(rv); - if (NS_WARN_IF(rv.Failed())) { - status = rv.StealNSResult(); - } } } @@ -2163,8 +2157,7 @@ XMLHttpRequestMainThread::OnStopRequest(nsIRequest *request, nsISupports *ctxt, mResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer)) { // set the capacity down to the actual length, to realloc back // down to the actual size - mLoadTotal = mArrayBufferBuilder.length(); - if (!mArrayBufferBuilder.setCapacity(mLoadTotal)) { + if (!mArrayBufferBuilder.setCapacity(mArrayBufferBuilder.length())) { // this should never happen! status = NS_ERROR_UNEXPECTED; } @@ -2268,12 +2261,6 @@ XMLHttpRequestMainThread::ChangeStateToDone() mTimeoutTimer->Cancel(); } - if (mLoadTransferred) { - mLoadTotal = mLoadTransferred; - } else { - mLoadTotal = -1; - } - // Per spec, fire the last download progress event, if any, // before readystatechange=4/done. (Note that 0-sized responses // will have not sent a progress event yet, so one must be sent here). @@ -3412,9 +3399,8 @@ XMLHttpRequestMainThread::OnProgress(nsIRequest *aRequest, nsISupports *aContext StartProgressEventTimer(); } } else { - mLoadTotal = lengthComputable ? aProgressMax : -1; + mLoadTotal = aProgressMax; mLoadTransferred = aProgress; - // OnDataAvailable() handles mProgressSinceLastProgressEvent // for the download phase. } @@ -3631,6 +3617,7 @@ XMLHttpRequestMainThread::HandleProgressTimerCallback() mUploadTransferred, mUploadTotal); } } else { + FireReadystatechangeEvent(); DispatchProgressEvent(this, ProgressEventType::progress, mLoadTransferred, mLoadTotal); } @@ -3809,12 +3796,6 @@ XMLHttpRequestMainThread::BlobStoreCompleted(MutableBlobStorage* aBlobStorage, mResponseBlob = aBlob; mBlobStorage = nullptr; - ErrorResult rv; - mLoadTotal = mResponseBlob->GetSize(rv); - if (NS_WARN_IF(rv.Failed())) { - rv.SuppressException(); - } - ChangeStateToDone(); } diff --git a/editor/libeditor/CSSEditUtils.cpp b/editor/libeditor/CSSEditUtils.cpp index 5199838c01e7..8e2effdf1936 100644 --- a/editor/libeditor/CSSEditUtils.cpp +++ b/editor/libeditor/CSSEditUtils.cpp @@ -319,6 +319,15 @@ bool CSSEditUtils::IsCSSEditableProperty(nsINode* aNode, nsIAtom* aProperty, const nsAString* aAttribute) +{ + nsCOMPtr attribute = aAttribute ? NS_Atomize(*aAttribute) : nullptr; + return IsCSSEditableProperty(aNode, aProperty, attribute); +} + +bool +CSSEditUtils::IsCSSEditableProperty(nsINode* aNode, + nsIAtom* aProperty, + nsIAtom* aAttribute) { MOZ_ASSERT(aNode); @@ -336,13 +345,12 @@ CSSEditUtils::IsCSSEditableProperty(nsINode* aNode, nsGkAtoms::u == aProperty || nsGkAtoms::strike == aProperty || (nsGkAtoms::font == aProperty && aAttribute && - (aAttribute->EqualsLiteral("color") || - aAttribute->EqualsLiteral("face")))) { + (aAttribute == nsGkAtoms::color || aAttribute == nsGkAtoms::face))) { return true; } // ALIGN attribute on elements supporting it - if (aAttribute && (aAttribute->EqualsLiteral("align")) && + if (aAttribute == nsGkAtoms::align && node->IsAnyOfHTMLElements(nsGkAtoms::div, nsGkAtoms::p, nsGkAtoms::h1, @@ -365,7 +373,7 @@ CSSEditUtils::IsCSSEditableProperty(nsINode* aNode, return true; } - if (aAttribute && (aAttribute->EqualsLiteral("valign")) && + if (aAttribute == nsGkAtoms::valign && node->IsAnyOfHTMLElements(nsGkAtoms::col, nsGkAtoms::colgroup, nsGkAtoms::tbody, @@ -378,59 +386,52 @@ CSSEditUtils::IsCSSEditableProperty(nsINode* aNode, } // attributes TEXT, BACKGROUND and BGCOLOR on BODY - if (aAttribute && node->IsHTMLElement(nsGkAtoms::body) && - (aAttribute->EqualsLiteral("text") - || aAttribute->EqualsLiteral("background") - || aAttribute->EqualsLiteral("bgcolor"))) { + if (node->IsHTMLElement(nsGkAtoms::body) && + (aAttribute == nsGkAtoms::text || aAttribute == nsGkAtoms::background || + aAttribute == nsGkAtoms::bgcolor)) { return true; } // attribute BGCOLOR on other elements - if (aAttribute && aAttribute->EqualsLiteral("bgcolor")) { + if (aAttribute == nsGkAtoms::bgcolor) { return true; } // attributes HEIGHT, WIDTH and NOWRAP on TD and TH - if (aAttribute && - node->IsAnyOfHTMLElements(nsGkAtoms::td, nsGkAtoms::th) && - (aAttribute->EqualsLiteral("height") - || aAttribute->EqualsLiteral("width") - || aAttribute->EqualsLiteral("nowrap"))) { + if (node->IsAnyOfHTMLElements(nsGkAtoms::td, nsGkAtoms::th) && + (aAttribute == nsGkAtoms::height || aAttribute == nsGkAtoms::width || + aAttribute == nsGkAtoms::nowrap)) { return true; } // attributes HEIGHT and WIDTH on TABLE - if (aAttribute && node->IsHTMLElement(nsGkAtoms::table) && - (aAttribute->EqualsLiteral("height") - || aAttribute->EqualsLiteral("width"))) { + if (node->IsHTMLElement(nsGkAtoms::table) && + (aAttribute == nsGkAtoms::height || aAttribute == nsGkAtoms::width)) { return true; } // attributes SIZE and WIDTH on HR - if (aAttribute && node->IsHTMLElement(nsGkAtoms::hr) && - (aAttribute->EqualsLiteral("size") - || aAttribute->EqualsLiteral("width"))) { + if (node->IsHTMLElement(nsGkAtoms::hr) && + (aAttribute == nsGkAtoms::size || aAttribute == nsGkAtoms::width)) { return true; } // attribute TYPE on OL UL LI - if (aAttribute && - node->IsAnyOfHTMLElements(nsGkAtoms::ol, nsGkAtoms::ul, + if (node->IsAnyOfHTMLElements(nsGkAtoms::ol, nsGkAtoms::ul, nsGkAtoms::li) && - aAttribute->EqualsLiteral("type")) { + aAttribute == nsGkAtoms::type) { return true; } - if (aAttribute && node->IsHTMLElement(nsGkAtoms::img) && - (aAttribute->EqualsLiteral("border") - || aAttribute->EqualsLiteral("width") - || aAttribute->EqualsLiteral("height"))) { + if (node->IsHTMLElement(nsGkAtoms::img) && + (aAttribute == nsGkAtoms::border || aAttribute == nsGkAtoms::width || + aAttribute == nsGkAtoms::height)) { return true; } // other elements that we can align using CSS even if they // can't carry the html ALIGN attribute - if (aAttribute && aAttribute->EqualsLiteral("align") && + if (aAttribute == nsGkAtoms::align && node->IsAnyOfHTMLElements(nsGkAtoms::ul, nsGkAtoms::ol, nsGkAtoms::dl, @@ -818,7 +819,7 @@ void CSSEditUtils::GenerateCSSDeclarationsFromHTMLStyle( Element* aElement, nsIAtom* aHTMLProperty, - const nsAString* aAttribute, + nsIAtom* aAttribute, const nsAString* aValue, nsTArray& cssPropertyArray, nsTArray& cssValueArray, @@ -838,21 +839,20 @@ CSSEditUtils::GenerateCSSDeclarationsFromHTMLStyle( } else if (nsGkAtoms::tt == aHTMLProperty) { equivTable = ttEquivTable; } else if (aAttribute) { - if (nsGkAtoms::font == aHTMLProperty && - aAttribute->EqualsLiteral("color")) { + if (nsGkAtoms::font == aHTMLProperty && aAttribute == nsGkAtoms::color) { equivTable = fontColorEquivTable; } else if (nsGkAtoms::font == aHTMLProperty && - aAttribute->EqualsLiteral("face")) { + aAttribute == nsGkAtoms::face) { equivTable = fontFaceEquivTable; - } else if (aAttribute->EqualsLiteral("bgcolor")) { + } else if (aAttribute == nsGkAtoms::bgcolor) { equivTable = bgcolorEquivTable; - } else if (aAttribute->EqualsLiteral("background")) { + } else if (aAttribute == nsGkAtoms::background) { equivTable = backgroundImageEquivTable; - } else if (aAttribute->EqualsLiteral("text")) { + } else if (aAttribute == nsGkAtoms::text) { equivTable = textColorEquivTable; - } else if (aAttribute->EqualsLiteral("border")) { + } else if (aAttribute == nsGkAtoms::border) { equivTable = borderEquivTable; - } else if (aAttribute->EqualsLiteral("align")) { + } else if (aAttribute == nsGkAtoms::align) { if (aElement->IsHTMLElement(nsGkAtoms::table)) { equivTable = tableAlignEquivTable; } else if (aElement->IsHTMLElement(nsGkAtoms::hr)) { @@ -863,17 +863,17 @@ CSSEditUtils::GenerateCSSDeclarationsFromHTMLStyle( } else { equivTable = textAlignEquivTable; } - } else if (aAttribute->EqualsLiteral("valign")) { + } else if (aAttribute == nsGkAtoms::valign) { equivTable = verticalAlignEquivTable; - } else if (aAttribute->EqualsLiteral("nowrap")) { + } else if (aAttribute == nsGkAtoms::nowrap) { equivTable = nowrapEquivTable; - } else if (aAttribute->EqualsLiteral("width")) { + } else if (aAttribute == nsGkAtoms::width) { equivTable = widthEquivTable; - } else if (aAttribute->EqualsLiteral("height") || + } else if (aAttribute == nsGkAtoms::height || (aElement->IsHTMLElement(nsGkAtoms::hr) && - aAttribute->EqualsLiteral("size"))) { + aAttribute == nsGkAtoms::size)) { equivTable = heightEquivTable; - } else if (aAttribute->EqualsLiteral("type") && + } else if (aAttribute == nsGkAtoms::type && aElement->IsAnyOfHTMLElements(nsGkAtoms::ol, nsGkAtoms::ul, nsGkAtoms::li)) { @@ -890,40 +890,46 @@ CSSEditUtils::GenerateCSSDeclarationsFromHTMLStyle( // aValue for the node, and return in aCount the number of CSS properties set // by the call. The Element version returns aCount instead. int32_t -CSSEditUtils::SetCSSEquivalentToHTMLStyle(Element* aElement, +CSSEditUtils::SetCSSEquivalentToHTMLStyle(nsIDOMNode* aNode, nsIAtom* aProperty, const nsAString* aAttribute, const nsAString* aValue, bool aSuppressTransaction) { - MOZ_ASSERT(aElement && aProperty); MOZ_ASSERT_IF(aAttribute, aValue); - int32_t count; // This can only fail if SetCSSProperty fails, which should only happen if // something is pretty badly wrong. In this case we assert so that hopefully // someone will notice, but there's nothing more sensible to do than just // return the count and carry on. - nsresult rv = SetCSSEquivalentToHTMLStyle(aElement->AsDOMNode(), - aProperty, aAttribute, - aValue, &count, - aSuppressTransaction); - NS_ASSERTION(NS_SUCCEEDED(rv), "SetCSSEquivalentToHTMLStyle failed"); - NS_ENSURE_SUCCESS(rv, count); - return count; + nsCOMPtr element = do_QueryInterface(aNode); + return SetCSSEquivalentToHTMLStyle(element, + aProperty, aAttribute, + aValue, aSuppressTransaction); } -nsresult -CSSEditUtils::SetCSSEquivalentToHTMLStyle(nsIDOMNode* aNode, +int32_t +CSSEditUtils::SetCSSEquivalentToHTMLStyle(Element* aElement, nsIAtom* aHTMLProperty, const nsAString* aAttribute, const nsAString* aValue, - int32_t* aCount, bool aSuppressTransaction) { - nsCOMPtr element = do_QueryInterface(aNode); - *aCount = 0; - if (!element || !IsCSSEditableProperty(element, aHTMLProperty, aAttribute)) { - return NS_OK; + nsCOMPtr attribute = aAttribute ? NS_Atomize(*aAttribute) : nullptr; + return SetCSSEquivalentToHTMLStyle(aElement, aHTMLProperty, attribute, + aValue, aSuppressTransaction); +} + +int32_t +CSSEditUtils::SetCSSEquivalentToHTMLStyle(Element* aElement, + nsIAtom* aHTMLProperty, + nsIAtom* aAttribute, + const nsAString* aValue, + bool aSuppressTransaction) +{ + MOZ_ASSERT(aElement); + + if (!IsCSSEditableProperty(aElement, aHTMLProperty, aAttribute)) { + return 0; } // we can apply the styles only if the node is an element and if we have @@ -932,18 +938,20 @@ CSSEditUtils::SetCSSEquivalentToHTMLStyle(nsIDOMNode* aNode, // Find the CSS equivalence to the HTML style nsTArray cssPropertyArray; nsTArray cssValueArray; - GenerateCSSDeclarationsFromHTMLStyle(element, aHTMLProperty, aAttribute, + GenerateCSSDeclarationsFromHTMLStyle(aElement, aHTMLProperty, aAttribute, aValue, cssPropertyArray, cssValueArray, false); // set the individual CSS inline styles - *aCount = cssPropertyArray.Length(); - for (int32_t index = 0; index < *aCount; index++) { - nsresult rv = SetCSSProperty(*element, *cssPropertyArray[index], + size_t count = cssPropertyArray.Length(); + for (size_t index = 0; index < count; index++) { + nsresult rv = SetCSSProperty(*aElement, *cssPropertyArray[index], cssValueArray[index], aSuppressTransaction); - NS_ENSURE_SUCCESS(rv, rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return 0; + } } - return NS_OK; + return count; } // Remove from aNode the CSS inline style equivalent to HTMLProperty/aAttribute/aValue for the node @@ -955,20 +963,22 @@ CSSEditUtils::RemoveCSSEquivalentToHTMLStyle(nsIDOMNode* aNode, bool aSuppressTransaction) { nsCOMPtr element = do_QueryInterface(aNode); - NS_ENSURE_TRUE(element, NS_OK); + nsCOMPtr attribute = aAttribute ? NS_Atomize(*aAttribute) : nullptr; - return RemoveCSSEquivalentToHTMLStyle(element, aHTMLProperty, aAttribute, + return RemoveCSSEquivalentToHTMLStyle(element, aHTMLProperty, attribute, aValue, aSuppressTransaction); } nsresult CSSEditUtils::RemoveCSSEquivalentToHTMLStyle(Element* aElement, nsIAtom* aHTMLProperty, - const nsAString* aAttribute, + nsIAtom* aAttribute, const nsAString* aValue, bool aSuppressTransaction) { - MOZ_ASSERT(aElement); + if (NS_WARN_IF(!aElement)) { + return NS_OK; + } if (!IsCSSEditableProperty(aElement, aHTMLProperty, aAttribute)) { return NS_OK; @@ -1003,7 +1013,7 @@ CSSEditUtils::RemoveCSSEquivalentToHTMLStyle(Element* aElement, nsresult CSSEditUtils::GetCSSEquivalentToHTMLInlineStyleSet(nsINode* aNode, nsIAtom* aHTMLProperty, - const nsAString* aAttribute, + nsIAtom* aAttribute, nsAString& aValueString, StyleType aStyleType) { @@ -1020,7 +1030,8 @@ CSSEditUtils::GetCSSEquivalentToHTMLInlineStyleSet(nsINode* aNode, nsTArray cssValueArray; // get the CSS equivalence with last param true indicating we want only the // "gettable" properties - GenerateCSSDeclarationsFromHTMLStyle(theElement, aHTMLProperty, aAttribute, nullptr, + GenerateCSSDeclarationsFromHTMLStyle(theElement, aHTMLProperty, aAttribute, + nullptr, cssPropertyArray, cssValueArray, true); int32_t count = cssPropertyArray.Length(); for (int32_t index = 0; index < count; index++) { @@ -1066,48 +1077,58 @@ CSSEditUtils::IsCSSEquivalentToHTMLInlineStyleSet(nsINode* aNode, StyleType aStyleType) { MOZ_ASSERT(aNode && aProperty); - bool isSet; - nsresult rv = IsCSSEquivalentToHTMLInlineStyleSet(aNode->AsDOMNode(), - aProperty, aAttribute, - isSet, aValue, aStyleType); - NS_ENSURE_SUCCESS(rv, false); - return isSet; + nsCOMPtr attribute = aAttribute ? NS_Atomize(*aAttribute) : nullptr; + return IsCSSEquivalentToHTMLInlineStyleSet(aNode, + aProperty, attribute, + aValue, aStyleType); } -nsresult +bool +CSSEditUtils::IsCSSEquivalentToHTMLInlineStyleSet(nsIDOMNode* aNode, + nsIAtom* aProperty, + const nsAString* aAttribute, + nsAString& aValue, + StyleType aStyleType) +{ + MOZ_ASSERT(aNode && aProperty); + nsCOMPtr node = do_QueryInterface(aNode); + nsCOMPtr attribute = aAttribute ? NS_Atomize(*aAttribute) : nullptr; + return IsCSSEquivalentToHTMLInlineStyleSet(node, aProperty, attribute, + aValue, aStyleType); +} + +bool CSSEditUtils::IsCSSEquivalentToHTMLInlineStyleSet( - nsIDOMNode* aNode, + nsINode* aNode, nsIAtom* aHTMLProperty, - const nsAString* aHTMLAttribute, - bool& aIsSet, + nsIAtom* aHTMLAttribute, nsAString& valueString, StyleType aStyleType) { - NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER); + NS_ENSURE_TRUE(aNode, false); nsAutoString htmlValueString(valueString); - aIsSet = false; - nsCOMPtr node = do_QueryInterface(aNode); + bool isSet = false; do { valueString.Assign(htmlValueString); // get the value of the CSS equivalent styles nsresult rv = - GetCSSEquivalentToHTMLInlineStyleSet(node, aHTMLProperty, aHTMLAttribute, + GetCSSEquivalentToHTMLInlineStyleSet(aNode, aHTMLProperty, aHTMLAttribute, valueString, aStyleType); - NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_SUCCESS(rv, false); // early way out if we can if (valueString.IsEmpty()) { - return NS_OK; + return isSet; } if (nsGkAtoms::b == aHTMLProperty) { if (valueString.EqualsLiteral("bold")) { - aIsSet = true; + isSet = true; } else if (valueString.EqualsLiteral("normal")) { - aIsSet = false; + isSet = false; } else if (valueString.EqualsLiteral("bolder")) { - aIsSet = true; + isSet = true; valueString.AssignLiteral("bold"); } else { int32_t weight = 0; @@ -1115,32 +1136,31 @@ CSSEditUtils::IsCSSEquivalentToHTMLInlineStyleSet( nsAutoString value(valueString); weight = value.ToInteger(&errorCode); if (400 < weight) { - aIsSet = true; + isSet = true; valueString.AssignLiteral("bold"); } else { - aIsSet = false; + isSet = false; valueString.AssignLiteral("normal"); } } } else if (nsGkAtoms::i == aHTMLProperty) { if (valueString.EqualsLiteral("italic") || valueString.EqualsLiteral("oblique")) { - aIsSet = true; + isSet = true; } } else if (nsGkAtoms::u == aHTMLProperty) { nsAutoString val; val.AssignLiteral("underline"); - aIsSet = ChangeStyleTransaction::ValueIncludes(valueString, val); + isSet = ChangeStyleTransaction::ValueIncludes(valueString, val); } else if (nsGkAtoms::strike == aHTMLProperty) { nsAutoString val; val.AssignLiteral("line-through"); - aIsSet = ChangeStyleTransaction::ValueIncludes(valueString, val); - } else if (aHTMLAttribute && - ((nsGkAtoms::font == aHTMLProperty && - aHTMLAttribute->EqualsLiteral("color")) || - aHTMLAttribute->EqualsLiteral("bgcolor"))) { + isSet = ChangeStyleTransaction::ValueIncludes(valueString, val); + } else if ((nsGkAtoms::font == aHTMLProperty && + aHTMLAttribute == nsGkAtoms::color) || + aHTMLAttribute == nsGkAtoms::bgcolor) { if (htmlValueString.IsEmpty()) { - aIsSet = true; + isSet = true; } else { nscolor rgba; nsAutoString subStr; @@ -1174,54 +1194,53 @@ CSSEditUtils::IsCSSEquivalentToHTMLInlineStyleSet( htmlColor.Append(char16_t(')')); } - aIsSet = htmlColor.Equals(valueString, - nsCaseInsensitiveStringComparator()); + isSet = htmlColor.Equals(valueString, + nsCaseInsensitiveStringComparator()); } else { - aIsSet = htmlValueString.Equals(valueString, - nsCaseInsensitiveStringComparator()); + isSet = htmlValueString.Equals(valueString, + nsCaseInsensitiveStringComparator()); } } } else if (nsGkAtoms::tt == aHTMLProperty) { - aIsSet = StringBeginsWith(valueString, NS_LITERAL_STRING("monospace")); + isSet = StringBeginsWith(valueString, NS_LITERAL_STRING("monospace")); } else if (nsGkAtoms::font == aHTMLProperty && aHTMLAttribute && - aHTMLAttribute->EqualsLiteral("face")) { + aHTMLAttribute == nsGkAtoms::face) { if (!htmlValueString.IsEmpty()) { const char16_t commaSpace[] = { char16_t(','), char16_t(' '), 0 }; const char16_t comma[] = { char16_t(','), 0 }; htmlValueString.ReplaceSubstring(commaSpace, comma); nsAutoString valueStringNorm(valueString); valueStringNorm.ReplaceSubstring(commaSpace, comma); - aIsSet = htmlValueString.Equals(valueStringNorm, - nsCaseInsensitiveStringComparator()); + isSet = htmlValueString.Equals(valueStringNorm, + nsCaseInsensitiveStringComparator()); } else { - aIsSet = true; + isSet = true; } - return NS_OK; - } else if (aHTMLAttribute && aHTMLAttribute->EqualsLiteral("align")) { - aIsSet = true; + return isSet; + } else if (aHTMLAttribute == nsGkAtoms::align) { + isSet = true; } else { - aIsSet = false; - return NS_OK; + return false; } if (!htmlValueString.IsEmpty() && htmlValueString.Equals(valueString, nsCaseInsensitiveStringComparator())) { - aIsSet = true; + isSet = true; } if (htmlValueString.EqualsLiteral("-moz-editor-invert-value")) { - aIsSet = !aIsSet; + isSet = !isSet; } if (nsGkAtoms::u == aHTMLProperty || nsGkAtoms::strike == aHTMLProperty) { // unfortunately, the value of the text-decoration property is not inherited. // that means that we have to look at ancestors of node to see if they are underlined - node = node->GetParentElement(); // set to null if it's not a dom element + aNode = aNode->GetParentElement(); // set to null if it's not a dom element } } while ((nsGkAtoms::u == aHTMLProperty || - nsGkAtoms::strike == aHTMLProperty) && !aIsSet && node); - return NS_OK; + nsGkAtoms::strike == aHTMLProperty) && !isSet && aNode); + return isSet; } void diff --git a/editor/libeditor/CSSEditUtils.h b/editor/libeditor/CSSEditUtils.h index 0b9a12952743..5129ab88dd18 100644 --- a/editor/libeditor/CSSEditUtils.h +++ b/editor/libeditor/CSSEditUtils.h @@ -90,6 +90,8 @@ public: */ bool IsCSSEditableProperty(nsINode* aNode, nsIAtom* aProperty, const nsAString* aAttribute); + bool IsCSSEditableProperty(nsINode* aNode, nsIAtom* aProperty, + nsIAtom* aAttribute); /** * Adds/remove a CSS declaration to the STYLE atrribute carried by a given @@ -188,14 +190,14 @@ public: * * @param aNode [IN] A DOM node. * @param aHTMLProperty [IN] An atom containing an HTML property. - * @param aAttribute [IN] A pointer to an attribute name or nullptr if + * @param aAttribute [IN] An atom of attribute name or nullptr if * irrelevant. * @param aValueString [OUT] The list of CSS values. * @param aStyleType [IN] eSpecified or eComputed. */ nsresult GetCSSEquivalentToHTMLInlineStyleSet(nsINode* aNode, nsIAtom* aHTMLProperty, - const nsAString* aAttribute, + nsIAtom* aAttribute, nsAString& aValueString, StyleType aStyleType); @@ -205,16 +207,20 @@ public: * * @param aNode [IN] A DOM node. * @param aHTMLProperty [IN] An atom containing an HTML property. - * @param aAttribute [IN] A pointer to an attribute name or nullptr if - * irrelevant. - * @param aIsSet [OUT] A boolean being true if the css properties are - * set. + * @param aAttribute [IN] A pointer/atom to an attribute name or nullptr + * if irrelevant. * @param aValueString [IN/OUT] The attribute value (in) the list of CSS * values (out). * @param aStyleType [IN] eSpecified or eComputed. - * - * The nsIContent variant returns aIsSet instead of using an out parameter. + * @return A boolean being true if the css properties are + * set. */ + bool IsCSSEquivalentToHTMLInlineStyleSet(nsINode* aContent, + nsIAtom* aProperty, + nsIAtom* aAttribute, + nsAString& aValue, + StyleType aStyleType); + bool IsCSSEquivalentToHTMLInlineStyleSet(nsINode* aContent, nsIAtom* aProperty, const nsAString* aAttribute, @@ -227,12 +233,11 @@ public: nsAString& aValue, StyleType aStyleType); - nsresult IsCSSEquivalentToHTMLInlineStyleSet(nsIDOMNode* aNode, - nsIAtom* aHTMLProperty, - const nsAString* aAttribute, - bool& aIsSet, - nsAString& aValueString, - StyleType aStyleType); + bool IsCSSEquivalentToHTMLInlineStyleSet(nsIDOMNode* aNode, + nsIAtom* aProperty, + const nsAString* aAttribute, + nsAString& aValue, + StyleType aStyleType); /** * Adds to the node the CSS inline styles equivalent to the HTML style @@ -240,27 +245,29 @@ public: * * @param aNode [IN] A DOM node. * @param aHTMLProperty [IN] An atom containing an HTML property. - * @param aAttribute [IN] A pointer to an attribute name or nullptr if - * irrelevant. + * @param aAttribute [IN] A pointer/atom to an attribute name or nullptr + * if irrelevant. * @param aValue [IN] The attribute value. - * @param aCount [OUT] The number of CSS properties set by the call. * @param aSuppressTransaction [IN] A boolean indicating, when true, * that no transaction should be recorded. * - * aCount is returned by the dom::Element variant instead of being an out - * parameter. + * @return The number of CSS properties set by the call. */ + int32_t SetCSSEquivalentToHTMLStyle(dom::Element* aElement, + nsIAtom* aProperty, + nsIAtom* aAttribute, + const nsAString* aValue, + bool aSuppressTransaction); int32_t SetCSSEquivalentToHTMLStyle(dom::Element* aElement, nsIAtom* aProperty, const nsAString* aAttribute, const nsAString* aValue, bool aSuppressTransaction); - nsresult SetCSSEquivalentToHTMLStyle(nsIDOMNode* aNode, - nsIAtom* aHTMLProperty, - const nsAString* aAttribute, - const nsAString* aValue, - int32_t* aCount, - bool aSuppressTransaction); + int32_t SetCSSEquivalentToHTMLStyle(nsIDOMNode* aNode, + nsIAtom* aHTMLProperty, + const nsAString* aAttribute, + const nsAString* aValue, + bool aSuppressTransaction); /** * Removes from the node the CSS inline styles equivalent to the HTML style. @@ -284,7 +291,7 @@ public: * * @param aElement [IN] A DOM Element (must not be null). * @param aHTMLProperty [IN] An atom containing an HTML property. - * @param aAttribute [IN] A pointer to an attribute name or nullptr if + * @param aAttribute [IN] An atom to an attribute name or nullptr if * irrelevant. * @param aValue [IN] The attribute value. * @param aSuppressTransaction [IN] A boolean indicating, when true, @@ -292,7 +299,7 @@ public: */ nsresult RemoveCSSEquivalentToHTMLStyle(dom::Element* aElement, nsIAtom* aHTMLProperty, - const nsAString* aAttribute, + nsIAtom* aAttribute, const nsAString* aValue, bool aSuppressTransaction); @@ -409,7 +416,7 @@ private: * * @param aNode [IN] The DOM node. * @param aHTMLProperty [IN] An atom containing an HTML property. - * @param aAttribute [IN] A pointer to an attribute name or nullptr + * @param aAttribute [IN] An atom to an attribute name or nullptr * if irrelevant * @param aValue [IN] The attribute value. * @param aPropertyArray [OUT] The array of CSS properties. @@ -422,7 +429,7 @@ private: */ void GenerateCSSDeclarationsFromHTMLStyle(dom::Element* aNode, nsIAtom* aHTMLProperty, - const nsAString* aAttribute, + nsIAtom* aAttribute, const nsAString* aValue, nsTArray& aPropertyArray, nsTArray& aValueArray, diff --git a/editor/libeditor/HTMLEditRules.cpp b/editor/libeditor/HTMLEditRules.cpp index 07a50f2a845a..45a902078346 100644 --- a/editor/libeditor/HTMLEditRules.cpp +++ b/editor/libeditor/HTMLEditRules.cpp @@ -838,19 +838,18 @@ HTMLEditRules::GetAlignment(bool* aMixed, NS_ENSURE_TRUE(nodeToExamine, NS_ERROR_NULL_POINTER); - NS_NAMED_LITERAL_STRING(typeAttrName, "align"); nsCOMPtr blockParent = htmlEditor->GetBlock(*nodeToExamine); NS_ENSURE_TRUE(blockParent, NS_ERROR_FAILURE); if (htmlEditor->IsCSSEnabled() && htmlEditor->mCSSEditUtils->IsCSSEditableProperty(blockParent, nullptr, - &typeAttrName)) { + nsGkAtoms::align)) { // We are in CSS mode and we know how to align this element with CSS nsAutoString value; // Let's get the value(s) of text-align or margin-left/margin-right htmlEditor->mCSSEditUtils->GetCSSEquivalentToHTMLInlineStyleSet( - blockParent, nullptr, &typeAttrName, value, CSSEditUtils::eComputed); + blockParent, nullptr, nsGkAtoms::align, value, CSSEditUtils::eComputed); if (value.EqualsLiteral("center") || value.EqualsLiteral("-moz-center") || value.EqualsLiteral("auto auto")) { @@ -4727,7 +4726,7 @@ HTMLEditRules::WillAlign(Selection& aSelection, NS_ENSURE_SUCCESS(rv, rv); if (useCSS) { htmlEditor->mCSSEditUtils->SetCSSEquivalentToHTMLStyle( - curNode->AsElement(), nullptr, &NS_LITERAL_STRING("align"), + curNode->AsElement(), nullptr, nsGkAtoms::align, &aAlignType, false); curDiv = nullptr; continue; @@ -7093,9 +7092,9 @@ HTMLEditRules::CacheInlineStyles(nsIDOMNode* aNode) isSet, &outValue); } else { NS_ENSURE_STATE(mHTMLEditor); - mHTMLEditor->mCSSEditUtils->IsCSSEquivalentToHTMLInlineStyleSet(aNode, - mCachedStyles[j].tag, &(mCachedStyles[j].attr), isSet, outValue, - CSSEditUtils::eComputed); + isSet = mHTMLEditor->mCSSEditUtils->IsCSSEquivalentToHTMLInlineStyleSet( + aNode, mCachedStyles[j].tag, &(mCachedStyles[j].attr), outValue, + CSSEditUtils::eComputed); } if (isSet) { mCachedStyles[j].mPresent = true; diff --git a/editor/libeditor/HTMLEditor.cpp b/editor/libeditor/HTMLEditor.cpp index 8b8ca4e5b2cb..5f1007a21156 100644 --- a/editor/libeditor/HTMLEditor.cpp +++ b/editor/libeditor/HTMLEditor.cpp @@ -4455,30 +4455,35 @@ HTMLEditor::SetAttributeOrEquivalent(nsIDOMElement* aElement, nsAutoScriptBlocker scriptBlocker; if (IsCSSEnabled() && mCSSEditUtils) { - int32_t count; - nsresult rv = - mCSSEditUtils->SetCSSEquivalentToHTMLStyle(aElement, nullptr, - &aAttribute, &aValue, - &count, + nsCOMPtr element = do_QueryInterface(aElement); + MOZ_ASSERT(element); + + nsCOMPtr attribute = NS_Atomize(aAttribute); + MOZ_ASSERT(attribute); + + int32_t count = + mCSSEditUtils->SetCSSEquivalentToHTMLStyle(element, nullptr, + attribute, &aValue, aSuppressTransaction); - NS_ENSURE_SUCCESS(rv, rv); if (count) { // we found an equivalence ; let's remove the HTML attribute itself if it is set nsAutoString existingValue; bool wasSet = false; - rv = GetAttributeValue(aElement, aAttribute, existingValue, &wasSet); + nsresult rv = + GetAttributeValue(aElement, aAttribute, existingValue, &wasSet); NS_ENSURE_SUCCESS(rv, rv); if (!wasSet) { return NS_OK; } - return aSuppressTransaction ? aElement->RemoveAttribute(aAttribute) : - RemoveAttribute(aElement, aAttribute); + return aSuppressTransaction ? + element->UnsetAttr(kNameSpaceID_None, attribute, true) : + RemoveAttribute(aElement, aAttribute); } // count is an integer that represents the number of CSS declarations applied to the // element. If it is zero, we found no equivalence in this implementation for the // attribute - if (aAttribute.EqualsLiteral("style")) { + if (attribute == nsGkAtoms::style) { // if it is the style attribute, just add the new value to the existing style // attribute's value nsAutoString existingValue; @@ -4489,14 +4494,15 @@ HTMLEditor::SetAttributeOrEquivalent(nsIDOMElement* aElement, existingValue.Append(' '); existingValue.Append(aValue); return aSuppressTransaction ? - aElement->SetAttribute(aAttribute, existingValue) : + element->SetAttr(kNameSpaceID_None, attribute, existingValue, true) : SetAttribute(aElement, aAttribute, existingValue); } // we have no CSS equivalence for this attribute and it is not the style // attribute; let's set it the good'n'old HTML way - return aSuppressTransaction ? aElement->SetAttribute(aAttribute, aValue) : - SetAttribute(aElement, aAttribute, aValue); + return aSuppressTransaction ? + element->SetAttr(kNameSpaceID_None, attribute, aValue, true) : + SetAttribute(aElement, aAttribute, aValue); } // we are not in an HTML+CSS editor; let's set the attribute the HTML way @@ -4518,7 +4524,7 @@ HTMLEditor::RemoveAttributeOrEquivalent(nsIDOMElement* aElement, if (IsCSSEnabled() && mCSSEditUtils) { nsresult rv = mCSSEditUtils->RemoveCSSEquivalentToHTMLStyle( - element, nullptr, &aAttribute, nullptr, aSuppressTransaction); + element, nullptr, attribute, nullptr, aSuppressTransaction); NS_ENSURE_SUCCESS(rv, rv); } @@ -4580,7 +4586,6 @@ HTMLEditor::SetCSSBackgroundColor(const nsAString& aColor) NS_ENSURE_SUCCESS(rv, rv); if (!cancel && !handled) { // Loop through the ranges in the selection - NS_NAMED_LITERAL_STRING(bgcolor, "bgcolor"); for (uint32_t i = 0; i < selection->RangeCount(); i++) { RefPtr range = selection->GetRangeAt(i); NS_ENSURE_TRUE(range, NS_ERROR_FAILURE); @@ -4599,13 +4604,15 @@ HTMLEditor::SetCSSBackgroundColor(const nsAString& aColor) if (blockParent && cachedBlockParent != blockParent) { cachedBlockParent = blockParent; mCSSEditUtils->SetCSSEquivalentToHTMLStyle(blockParent, nullptr, - &bgcolor, &aColor, false); + nsGkAtoms::bgcolor, + &aColor, false); } } else if (startNode == endNode && startNode->IsHTMLElement(nsGkAtoms::body) && isCollapsed) { // No block in the document, let's apply the background to the body mCSSEditUtils->SetCSSEquivalentToHTMLStyle(startNode->AsElement(), - nullptr, &bgcolor, &aColor, + nullptr, nsGkAtoms::bgcolor, + &aColor, false); } else if (startNode == endNode && (endOffset - startOffset == 1 || (!startOffset && !endOffset))) { @@ -4616,7 +4623,8 @@ HTMLEditor::SetCSSBackgroundColor(const nsAString& aColor) if (blockParent && cachedBlockParent != blockParent) { cachedBlockParent = blockParent; mCSSEditUtils->SetCSSEquivalentToHTMLStyle(blockParent, nullptr, - &bgcolor, &aColor, false); + nsGkAtoms::bgcolor, + &aColor, false); } } else { // Not the easy case. Range not contained in single text node. There @@ -4659,7 +4667,8 @@ HTMLEditor::SetCSSBackgroundColor(const nsAString& aColor) if (blockParent && cachedBlockParent != blockParent) { cachedBlockParent = blockParent; mCSSEditUtils->SetCSSEquivalentToHTMLStyle(blockParent, nullptr, - &bgcolor, &aColor, + nsGkAtoms::bgcolor, + &aColor, false); } } @@ -4670,7 +4679,8 @@ HTMLEditor::SetCSSBackgroundColor(const nsAString& aColor) if (blockParent && cachedBlockParent != blockParent) { cachedBlockParent = blockParent; mCSSEditUtils->SetCSSEquivalentToHTMLStyle(blockParent, nullptr, - &bgcolor, &aColor, + nsGkAtoms::bgcolor, + &aColor, false); } } @@ -4684,7 +4694,8 @@ HTMLEditor::SetCSSBackgroundColor(const nsAString& aColor) if (blockParent && cachedBlockParent != blockParent) { cachedBlockParent = blockParent; mCSSEditUtils->SetCSSEquivalentToHTMLStyle(blockParent, nullptr, - &bgcolor, &aColor, + nsGkAtoms::bgcolor, + &aColor, false); } } diff --git a/editor/libeditor/HTMLStyleEditor.cpp b/editor/libeditor/HTMLStyleEditor.cpp index ef06694941ab..eddc4954732e 100644 --- a/editor/libeditor/HTMLStyleEditor.cpp +++ b/editor/libeditor/HTMLStyleEditor.cpp @@ -427,7 +427,7 @@ HTMLEditor::SetInlinePropertyOnNodeImpl(nsIContent& aNode, mCSSEditUtils->IsCSSEditableProperty(&aNode, &aProperty, aAttribute)) || // bgcolor is always done using CSS - aAttribute->EqualsLiteral("bgcolor"); + attrAtom == nsGkAtoms::bgcolor; if (useCSS) { nsCOMPtr tmp; @@ -442,12 +442,9 @@ HTMLEditor::SetInlinePropertyOnNodeImpl(nsIContent& aNode, } // Add the CSS styles corresponding to the HTML style request - int32_t count; - nsresult rv = - mCSSEditUtils->SetCSSEquivalentToHTMLStyle(tmp->AsDOMNode(), - &aProperty, aAttribute, - &aValue, &count, false); - NS_ENSURE_SUCCESS(rv, rv); + mCSSEditUtils->SetCSSEquivalentToHTMLStyle(tmp, + &aProperty, attrAtom, + &aValue, false); return NS_OK; } @@ -572,8 +569,9 @@ HTMLEditor::SplitStyleAbovePoint(nsCOMPtr* aNode, // in this implementation for the node; let's check if it carries those // CSS styles nsAutoString firstValue; - mCSSEditUtils->IsCSSEquivalentToHTMLInlineStyleSet(GetAsDOMNode(node), - aProperty, aAttribute, isSet, firstValue, CSSEditUtils::eSpecified); + isSet = mCSSEditUtils->IsCSSEquivalentToHTMLInlineStyleSet( + node, aProperty, aAttribute, firstValue, + CSSEditUtils::eSpecified); } if (// node is the correct inline prop (aProperty && node->IsHTMLElement(aProperty)) || @@ -784,15 +782,17 @@ HTMLEditor::RemoveStyleInside(nsIContent& aNode, // the HTML style defined by aProperty/aAttribute has a CSS equivalence in // this implementation for the node aNode; let's check if it carries those // css styles + nsCOMPtr attribute = + aAttribute ? NS_Atomize(*aAttribute) : nullptr; nsAutoString propertyValue; bool isSet = mCSSEditUtils->IsCSSEquivalentToHTMLInlineStyleSet(&aNode, - aProperty, aAttribute, propertyValue, CSSEditUtils::eSpecified); + aProperty, attribute, propertyValue, CSSEditUtils::eSpecified); if (isSet && aNode.IsElement()) { // yes, tmp has the corresponding css declarations in its style attribute // let's remove them mCSSEditUtils->RemoveCSSEquivalentToHTMLStyle(aNode.AsElement(), aProperty, - aAttribute, + attribute, &propertyValue, false); // remove the node if it is a span or font, if its style attribute is diff --git a/embedding/components/webbrowserpersist/WebBrowserPersistDocumentParent.cpp b/embedding/components/webbrowserpersist/WebBrowserPersistDocumentParent.cpp index c417a860eb9f..c6c0b86b8388 100644 --- a/embedding/components/webbrowserpersist/WebBrowserPersistDocumentParent.cpp +++ b/embedding/components/webbrowserpersist/WebBrowserPersistDocumentParent.cpp @@ -7,6 +7,7 @@ #include "WebBrowserPersistDocumentParent.h" #include "mozilla/ipc/InputStreamUtils.h" +#include "mozilla/dom/PContentParent.h" #include "nsIInputStream.h" #include "nsThreadUtils.h" #include "WebBrowserPersistResourcesParent.h" @@ -84,8 +85,9 @@ WebBrowserPersistDocumentParent::RecvInitFailure(const nsresult& aFailure) mOnReady->OnError(aFailure); mOnReady = nullptr; // Warning: Send__delete__ deallocates this object. + IProtocol* mgr = Manager(); if (!Send__delete__(this)) { - return IPC_FAIL_NO_REASON(this); + return IPC_FAIL_NO_REASON(mgr); } return IPC_OK(); } diff --git a/gfx/layers/ipc/LayerTransactionParent.cpp b/gfx/layers/ipc/LayerTransactionParent.cpp index 77ebd158a521..a42184d9c401 100644 --- a/gfx/layers/ipc/LayerTransactionParent.cpp +++ b/gfx/layers/ipc/LayerTransactionParent.cpp @@ -163,8 +163,9 @@ mozilla::ipc::IPCResult LayerTransactionParent::RecvShutdown() { Destroy(); + IProtocol* mgr = Manager(); if (!Send__delete__(this)) { - return IPC_FAIL_NO_REASON(this); + return IPC_FAIL_NO_REASON(mgr); } return IPC_OK(); } diff --git a/gfx/thebes/gfxFontUtils.cpp b/gfx/thebes/gfxFontUtils.cpp index cb505e87bed7..db18ab6dfd37 100644 --- a/gfx/thebes/gfxFontUtils.cpp +++ b/gfx/thebes/gfxFontUtils.cpp @@ -139,9 +139,13 @@ gfxFontUtils::ReadCMAPTableFormat10(const uint8_t *aBuf, uint32_t aLength, } nsresult -gfxFontUtils::ReadCMAPTableFormat12(const uint8_t *aBuf, uint32_t aLength, - gfxSparseBitSet& aCharacterMap) +gfxFontUtils::ReadCMAPTableFormat12or13(const uint8_t *aBuf, uint32_t aLength, + gfxSparseBitSet& aCharacterMap) { + // Format 13 has the same structure as format 12, the only difference is + // the interpretation of the glyphID field. So we can share the code here + // that reads the table and just records character coverage. + // Ensure table is large enough that we can safely read the header NS_ENSURE_TRUE(aLength >= sizeof(Format12CmapHeader), NS_ERROR_GFX_CMAP_MALFORMED); @@ -149,9 +153,10 @@ gfxFontUtils::ReadCMAPTableFormat12(const uint8_t *aBuf, uint32_t aLength, // Sanity-check header fields const Format12CmapHeader *cmap12 = reinterpret_cast(aBuf); - NS_ENSURE_TRUE(uint16_t(cmap12->format) == 12, + NS_ENSURE_TRUE(uint16_t(cmap12->format) == 12 || + uint16_t(cmap12->format) == 13, NS_ERROR_GFX_CMAP_MALFORMED); - NS_ENSURE_TRUE(uint16_t(cmap12->reserved) == 0, + NS_ENSURE_TRUE(uint16_t(cmap12->reserved) == 0, NS_ERROR_GFX_CMAP_MALFORMED); uint32_t tablelen = cmap12->length; @@ -472,7 +477,7 @@ gfxFontUtils::FindPreferredSubtable(const uint8_t *aBuf, uint32_t aBufLength, keepFormat = format; *aTableOffset = offset; *aSymbolEncoding = false; - } else if ((format == 10 || format == 12) && + } else if ((format == 10 || format == 12 || format == 13) && acceptableUCS4Encoding(platformID, encodingID, keepFormat)) { keepFormat = format; *aTableOffset = offset; @@ -521,10 +526,11 @@ gfxFontUtils::ReadCMAP(const uint8_t *aBuf, uint32_t aBufLength, aCharacterMap); case 12: + case 13: aUnicodeFont = true; aSymbolFont = false; - return ReadCMAPTableFormat12(aBuf + offset, aBufLength - offset, - aCharacterMap); + return ReadCMAPTableFormat12or13(aBuf + offset, aBufLength - offset, + aCharacterMap); default: break; @@ -651,13 +657,17 @@ gfxFontUtils::MapCharToGlyphFormat10(const uint8_t *aBuf, uint32_t aCh) } uint32_t -gfxFontUtils::MapCharToGlyphFormat12(const uint8_t *aBuf, uint32_t aCh) +gfxFontUtils::MapCharToGlyphFormat12or13(const uint8_t *aBuf, uint32_t aCh) { + // The only difference between formats 12 and 13 is the interpretation of + // the glyphId field. So the code here uses the same "Format12" structures, + // etc., to handle both subtable formats. + const Format12CmapHeader *cmap12 = reinterpret_cast(aBuf); // We know that numGroups is within range for the subtable size - // because it was checked by ReadCMAPTableFormat12. + // because it was checked by ReadCMAPTableFormat12or13. uint32_t numGroups = cmap12->numGroups; // The array of groups immediately follows the subtable header. @@ -688,10 +698,13 @@ gfxFontUtils::MapCharToGlyphFormat12(const uint8_t *aBuf, uint32_t aCh) } // Check if the character is actually present in the range and return - // the corresponding glyph ID + // the corresponding glyph ID. Here is where formats 12 and 13 interpret + // the startGlyphId (12) or glyphId (13) field differently startCharCode = groups[range].startCharCode; if (startCharCode <= aCh && groups[range].endCharCode >= aCh) { - return groups[range].startGlyphId + aCh - startCharCode; + return uint16_t(cmap12->format) == 12 + ? uint16_t(groups[range].startGlyphId) + aCh - startCharCode + : uint16_t(groups[range].startGlyphId); } // Else it's not present, so return the .notdef glyph @@ -767,7 +780,8 @@ gfxFontUtils::MapCharToGlyph(const uint8_t *aCmapBuf, uint32_t aBufLength, gid = MapCharToGlyphFormat10(aCmapBuf + offset, aUnicode); break; case 12: - gid = MapCharToGlyphFormat12(aCmapBuf + offset, aUnicode); + case 13: + gid = MapCharToGlyphFormat12or13(aCmapBuf + offset, aUnicode); break; default: NS_WARNING("unsupported cmap format, glyphs will be missing"); @@ -793,8 +807,9 @@ gfxFontUtils::MapCharToGlyph(const uint8_t *aCmapBuf, uint32_t aBufLength, aUnicode); break; case 12: - varGID = MapCharToGlyphFormat12(aCmapBuf + offset, - aUnicode); + case 13: + varGID = MapCharToGlyphFormat12or13(aCmapBuf + offset, + aUnicode); break; } } diff --git a/gfx/thebes/gfxFontUtils.h b/gfx/thebes/gfxFontUtils.h index dd6a76558f3e..d5c7c3ae02dd 100644 --- a/gfx/thebes/gfxFontUtils.h +++ b/gfx/thebes/gfxFontUtils.h @@ -781,8 +781,8 @@ public: gfxSparseBitSet& aCharacterMap); static nsresult - ReadCMAPTableFormat12(const uint8_t *aBuf, uint32_t aLength, - gfxSparseBitSet& aCharacterMap); + ReadCMAPTableFormat12or13(const uint8_t *aBuf, uint32_t aLength, + gfxSparseBitSet& aCharacterMap); static nsresult ReadCMAPTableFormat4(const uint8_t *aBuf, uint32_t aLength, @@ -810,7 +810,7 @@ public: MapCharToGlyphFormat10(const uint8_t *aBuf, uint32_t aCh); static uint32_t - MapCharToGlyphFormat12(const uint8_t *aBuf, uint32_t aCh); + MapCharToGlyphFormat12or13(const uint8_t *aBuf, uint32_t aCh); static uint16_t MapUVSToGlyphFormat14(const uint8_t *aBuf, uint32_t aCh, uint32_t aVS); diff --git a/gfx/thebes/gfxHarfBuzzShaper.cpp b/gfx/thebes/gfxHarfBuzzShaper.cpp index 1f472f88dd0d..d01171158b62 100644 --- a/gfx/thebes/gfxHarfBuzzShaper.cpp +++ b/gfx/thebes/gfxHarfBuzzShaper.cpp @@ -131,8 +131,10 @@ gfxHarfBuzzShaper::GetNominalGlyph(hb_codepoint_t unicode) const unicode); break; case 12: - gid = gfxFontUtils::MapCharToGlyphFormat12(data + mSubtableOffset, - unicode); + case 13: + gid = + gfxFontUtils::MapCharToGlyphFormat12or13(data + mSubtableOffset, + unicode); break; default: NS_WARNING("unsupported cmap format, glyphs will be missing"); @@ -190,8 +192,10 @@ gfxHarfBuzzShaper::GetVariationGlyph(hb_codepoint_t unicode, compat); break; case 12: - return gfxFontUtils::MapCharToGlyphFormat12(data + mSubtableOffset, - compat); + case 13: + return + gfxFontUtils::MapCharToGlyphFormat12or13(data + mSubtableOffset, + compat); break; } } diff --git a/ipc/ipdl/ipdl/lower.py b/ipc/ipdl/ipdl/lower.py index 83c33e1935f2..1122c7f0099a 100644 --- a/ipc/ipdl/ipdl/lower.py +++ b/ipc/ipdl/ipdl/lower.py @@ -4084,15 +4084,12 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): msgvar, stmts = self.makeMessage(md, errfnSendDtor, actorvar) sendok, sendstmts = self.sendAsync(md, msgvar, actorvar) - failif = StmtIf(ExprNot(sendok)) - failif.addifstmt(StmtReturn.FALSE) - method.addstmts( stmts + self.genVerifyMessage(md.decl.type.verify, md.params, errfnSendDtor, ExprVar('msg__')) + sendstmts - + [ failif, Whitespace.NL ] + + [ Whitespace.NL ] + self.dtorEpilogue(md, actor.var()) + [ StmtReturn(sendok) ]) diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index a9cba4c818b7..cb7ac7b0bfce 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -1341,21 +1341,6 @@ js::ReportIsNotFunction(JSContext* cx, HandleValue v) return ReportIsNotFunction(cx, v, -1); } -JS_FRIEND_API(void) -js::ReportASCIIErrorWithId(JSContext* cx, const char* msg, HandleId id) -{ - RootedValue idv(cx); - if (!JS_IdToValue(cx, id, &idv)) - return; - RootedString idstr(cx, JS::ToString(cx, idv)); - if (!idstr) - return; - JSAutoByteString bytes; - if (!bytes.encodeUtf8(cx, idstr)) - return; - JS_ReportErrorUTF8(cx, msg, bytes.ptr()); -} - #ifdef DEBUG bool js::HasObjectMovedOp(JSObject* obj) { diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index f648fb783923..1f4cf56f7fe7 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -2773,9 +2773,6 @@ SetPropertyIgnoringNamedGetter(JSContext* cx, JS::HandleObject obj, JS::HandleId JS::Handle ownDesc, JS::ObjectOpResult& result); -JS_FRIEND_API(void) -ReportASCIIErrorWithId(JSContext* cx, const char* msg, JS::HandleId id); - // This function is for one specific use case, please don't use this for anything else! extern JS_FRIEND_API(bool) ExecuteInGlobalAndReturnScope(JSContext* cx, JS::HandleObject obj, JS::HandleScript script, diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 1b34527025fa..dcd6b5a2e966 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -3270,7 +3270,8 @@ GCHelperState::startBackgroundThread(State newState, const AutoLockGC& lock, void GCHelperState::waitForBackgroundThread(js::AutoLockGC& lock) { - done.wait(lock.guard()); + while (isBackgroundSweeping()) + done.wait(lock.guard()); } void diff --git a/js/xpconnect/wrappers/AddonWrapper.cpp b/js/xpconnect/wrappers/AddonWrapper.cpp index eb1670b3aa06..568c69c2c581 100644 --- a/js/xpconnect/wrappers/AddonWrapper.cpp +++ b/js/xpconnect/wrappers/AddonWrapper.cpp @@ -23,6 +23,21 @@ using namespace JS; namespace xpc { +static inline void +ReportASCIIErrorWithId(JSContext* cx, const char* msg, HandleId id) +{ + RootedValue idv(cx); + if (!JS_IdToValue(cx, id, &idv)) + return; + RootedString idstr(cx, JS::ToString(cx, idv)); + if (!idstr) + return; + JSAutoByteString bytes; + if (!bytes.encodeUtf8(cx, idstr)) + return; + JS_ReportErrorUTF8(cx, msg, bytes.ptr()); +} + bool InterposeProperty(JSContext* cx, HandleObject target, const nsIID* iid, HandleId id, MutableHandle descriptor) @@ -231,7 +246,7 @@ AddonWrapper::defineProperty(JSContext* cx, HandleObject wrapper, HandleId if (!interpDesc.object()) return Base::defineProperty(cx, wrapper, id, desc, result); - js::ReportASCIIErrorWithId(cx, "unable to modify interposed property %s", id); + ReportASCIIErrorWithId(cx, "unable to modify interposed property %s", id); return false; } @@ -247,7 +262,7 @@ AddonWrapper::delete_(JSContext* cx, HandleObject wrapper, HandleId id, if (!desc.object()) return Base::delete_(cx, wrapper, id, result); - js::ReportASCIIErrorWithId(cx, "unable to delete interposed property %s", id); + ReportASCIIErrorWithId(cx, "unable to delete interposed property %s", id); return false; } diff --git a/layout/reftests/text/1320665-cmap-format-13-ref.html b/layout/reftests/text/1320665-cmap-format-13-ref.html new file mode 100644 index 000000000000..7f8314e4b95d --- /dev/null +++ b/layout/reftests/text/1320665-cmap-format-13-ref.html @@ -0,0 +1,12 @@ + + + + + + +PASS diff --git a/layout/reftests/text/1320665-cmap-format-13.html b/layout/reftests/text/1320665-cmap-format-13.html new file mode 100644 index 000000000000..7ffc472b5330 --- /dev/null +++ b/layout/reftests/text/1320665-cmap-format-13.html @@ -0,0 +1,22 @@ + + + + + + +P (fail) Aሴ顶ꯍS𐐀🌳S diff --git a/layout/reftests/text/reftest.list b/layout/reftests/text/reftest.list index b895ecd5acc3..1d1ce72d404f 100644 --- a/layout/reftests/text/reftest.list +++ b/layout/reftests/text/reftest.list @@ -185,6 +185,7 @@ HTTP(..) != fallback-mark-stacking-1.html fallback-mark-stacking-1-notref.html == 745555-2.html 745555-2-ref.html == 820255.html 820255-ref.html HTTP(..) != 1170688.html 1170688-ref.html +fails-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == 1320665-cmap-format-13.html 1320665-cmap-format-13-ref.html # see bug 1320665 comments 8-9 # ensure emoji chars don't render blank (bug 715798, bug 779042); # should at least render hexboxes if there's no font support diff --git a/layout/style/nsCSSPropList.h b/layout/style/nsCSSPropList.h index 0cd572fdb875..17c122f9aaf7 100644 --- a/layout/style/nsCSSPropList.h +++ b/layout/style/nsCSSPropList.h @@ -3878,7 +3878,7 @@ CSS_PROP_TEXT( VARIANT_INHERIT | VARIANT_LNCALC, nullptr, offsetof(nsStyleText, mTabSize), - eStyleAnimType_Discrete) + eStyleAnimType_Coord) CSS_PROP_TABLE( table-layout, table_layout, diff --git a/layout/style/test/test_transitions_per_property.html b/layout/style/test/test_transitions_per_property.html index 669fe83da295..1d85efb72fba 100644 --- a/layout/style/test/test_transitions_per_property.html +++ b/layout/style/test/test_transitions_per_property.html @@ -248,6 +248,8 @@ var supported_properties = { // test_length_percent_calc_transition. "stroke-width": [ test_length_transition_svg, test_percent_transition, test_length_clamped_svg, test_percent_clamped ], + "-moz-tab-size": [ test_float_zeroToOne_transition, + test_float_aboveOne_transition, test_length_clamped ], "text-decoration": [ test_color_shorthand_transition, test_true_currentcolor_shorthand_transition ], "text-decoration-color": [ test_color_transition, diff --git a/netwerk/protocol/websocket/WebSocketChannelParent.cpp b/netwerk/protocol/websocket/WebSocketChannelParent.cpp index b808ca52da7a..af293b8c5bb1 100644 --- a/netwerk/protocol/websocket/WebSocketChannelParent.cpp +++ b/netwerk/protocol/websocket/WebSocketChannelParent.cpp @@ -49,8 +49,9 @@ WebSocketChannelParent::RecvDeleteSelf() LOG(("WebSocketChannelParent::RecvDeleteSelf() %p\n", this)); mChannel = nullptr; mAuthProvider = nullptr; + IProtocol* mgr = Manager(); if (mIPCOpen && !Send__delete__(this)) { - return IPC_FAIL_NO_REASON(this); + return IPC_FAIL_NO_REASON(mgr); } return IPC_OK(); } diff --git a/testing/web-platform/meta/progress-events/tests/submissions/Samsung/firing-events-http-no-content-length.html.ini b/testing/web-platform/meta/progress-events/tests/submissions/Samsung/firing-events-http-no-content-length.html.ini deleted file mode 100644 index a8602ed85614..000000000000 --- a/testing/web-platform/meta/progress-events/tests/submissions/Samsung/firing-events-http-no-content-length.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[firing-events-http-no-content-length.html] - type: testharness - [ProgressEvent: firing events for HTTP with no Content-Length] - expected: FAIL - diff --git a/testing/web-platform/tests/XMLHttpRequest/resources/xmlhttprequest-event-order.js b/testing/web-platform/tests/XMLHttpRequest/resources/xmlhttprequest-event-order.js index 77fc0e784ef9..b6bb6cdf3c3e 100644 --- a/testing/web-platform/tests/XMLHttpRequest/resources/xmlhttprequest-event-order.js +++ b/testing/web-platform/tests/XMLHttpRequest/resources/xmlhttprequest-event-order.js @@ -22,53 +22,56 @@ } function getNextEvent(arr) { - var eventStr = arr.shift(); + var event = { str: arr.shift() }; // we can only handle strings, numbers (readystates) and undefined - if (eventStr === undefined) { + if (event.str === undefined) { return event; } - if (typeof eventStr !== "string") { - if (Number.isInteger(eventStr)) { - eventStr = "readystatechange(" + eventStr + ")"; + + if (typeof event.str !== "string") { + if (Number.isInteger(event.str)) { + event.state = event.str; + event.str = "readystatechange(" + event.str + ")"; } else { - throw "Test error: unexpected event type " + eventStr; + throw "Test error: unexpected event type " + event.str; } } // parse out the general type, loaded and total values - var type = eventStr.type = eventStr.split("(")[0].split(".").pop(); - eventStr.mayFollowOptionalProgressEvents = type == "progress" || - type == "load" || type == "abort" || type == "error"; - var loadedAndTotal = eventStr.match(/\((\d)+,(\d)+/); + var type = event.type = event.str.split("(")[0].split(".").pop(); + var loadedAndTotal = event.str.match(/.*\((\d+),(\d+),(true|false)\)/); if (loadedAndTotal) { - eventStr.loaded = parseInt(loadedAndTotal[0]); - eventStr.total = parseInt(loadedAndTotal[1]); + event.loaded = parseInt(loadedAndTotal[1]); + event.total = parseInt(loadedAndTotal[2]); + event.lengthComputable = loadedAndTotal[3] == "true"; } - return eventStr; + return event; } global.assert_xhr_event_order_matches = function(expected) { var recorded = recorded_xhr_events; var lastRecordedLoaded = -1; - while(expected.length && recorded.length) { var currentExpected = getNextEvent(expected), currentRecorded = getNextEvent(recorded); - // skip to the last progress event if we've hit one - while (recorded.length && currentRecorded.type == "progress") { - assert_greater(currentRecorded.loaded, lastRecordedLoaded, - "progress event 'loaded' values must only increase"); + // skip to the last progress event if we've hit one (note the next + // event after a progress event should be a LOADING readystatechange, + // if there are multiple progress events in a row). + while (recorded.length && currentRecorded.type == "progress" && + parseInt(recorded) === 3) { + assert_greater_than(currentRecorded.loaded, lastRecordedLoaded, + "progress event 'loaded' values must only increase"); lastRecordedLoaded = currentRecorded.loaded; - currentRecorded = getNextEvent(recorded); } - if (currentRecorded.type == "loadstart") { + if (currentRecorded.type == "loadend") { + recordedProgressCount = 0; lastRecordedLoaded = -1; } - assert_equals(currentRecorded, currentExpected); + assert_equals(currentRecorded.str, currentExpected.str); } if (recorded.length) { throw "\nUnexpected extra events: " + recorded.join(", "); diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 6c1cd9e69aab..44cf38d42130 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -9623,196 +9623,6 @@ "releaseChannelCollection": "opt-out", "description": "Reports whether the graphics sanity test passed an OS snapshot test. 0=Pass, 1=Fail, 2=Error, 3=Timed out." }, - "DEVTOOLS_HUD_JANK": { - "alert_emails": ["rnicoletti@mozilla.com","thills@mozilla.com"], - "expires_in_version": "52", - "kind": "exponential", - "keyed": true, - "description": "The duration which a thread is blocked in ms, keyed by appName.", - "high": 5000, - "n_buckets": 10 - }, - "DEVTOOLS_HUD_REFLOW_DURATION": { - "alert_emails": ["rnicoletti@mozilla.com","thills@mozilla.com"], - "expires_in_version": "52", - "kind": "exponential", - "keyed": true, - "description": "The duration a reflow takes in ms, keyed by appName.", - "high": 1000, - "n_buckets": 10 - }, - "DEVTOOLS_HUD_REFLOWS": { - "alert_emails": ["rnicoletti@mozilla.com","thills@mozilla.com"], - "expires_in_version": "52", - "kind": "count", - "keyed": true, - "description": "A count of the number of reflows, keyed by appName." - }, - "DEVTOOLS_HUD_SECURITY_CATEGORY": { - "alert_emails": ["rnicoletti@mozilla.com","thills@mozilla.com"], - "expires_in_version": "52", - "kind": "enumerated", - "keyed": true, - "description": "The security error enums, keyed by appName.", - "n_values": 8 - }, - "DEVTOOLS_HUD_ERRORS": { - "alert_emails": ["rnicoletti@mozilla.com","thills@mozilla.com"], - "expires_in_version": "52", - "kind": "count", - "keyed": true, - "description": "Number of errors, keyed by appName." - }, - "DEVTOOLS_HUD_WARNINGS": { - "alert_emails": ["rnicoletti@mozilla.com","thills@mozilla.com"], - "expires_in_version": "52", - "kind": "count", - "keyed": true, - "description": "Number of warnings, keyed by appName." - }, - "DEVTOOLS_HUD_USS": { - "alert_emails": ["rnicoletti@mozilla.com","thills@mozilla.com"], - "expires_in_version": "52", - "kind": "linear", - "keyed": true, - "low": 20000000, - "high": 100000000, - "n_buckets": 52, - "description": "The USS memory consumed by an application, keyed by appName." - }, - "DEVTOOLS_HUD_APP_STARTUP_TIME_CONTENTINTERACTIVE": { - "alert_emails": ["rnicoletti@mozilla.com","thills@mozilla.com"], - "expires_in_version": "52", - "kind": "linear", - "keyed": true, - "description": "The duration in ms between application launch and the 'contentInteractive' performance mark, keyed by appName.", - "high": 2000, - "n_buckets": 10 - }, - "DEVTOOLS_HUD_APP_STARTUP_TIME_NAVIGATIONINTERACTIVE": { - "alert_emails": ["rnicoletti@mozilla.com","thills@mozilla.com"], - "expires_in_version": "52", - "kind": "linear", - "keyed": true, - "description": "The duration in ms between application launch and the 'navigationInteractive' performance mark, keyed by appName.", - "high": 3000, - "n_buckets": 10 - }, - "DEVTOOLS_HUD_APP_STARTUP_TIME_NAVIGATIONLOADED": { - "alert_emails": ["rnicoletti@mozilla.com","thills@mozilla.com"], - "expires_in_version": "52", - "kind": "linear", - "keyed": true, - "description": "The duration in ms between application launch and the 'navigationLoaded' performance mark, keyed by appName.", - "high": 4000, - "n_buckets": 10 - }, - "DEVTOOLS_HUD_APP_STARTUP_TIME_VISUALLYLOADED": { - "alert_emails": ["rnicoletti@mozilla.com","thills@mozilla.com"], - "expires_in_version": "52", - "kind": "linear", - "keyed": true, - "description": "The duration in ms between application launch and the 'visuallyLoaded' performance mark, keyed by appName.", - "high": 5000, - "n_buckets": 10 - }, - "DEVTOOLS_HUD_APP_STARTUP_TIME_MEDIAENUMERATED": { - "alert_emails": ["rnicoletti@mozilla.com","thills@mozilla.com"], - "expires_in_version": "52", - "kind": "linear", - "keyed": true, - "description": "The duration in ms between application launch and the 'mediaEnumerated' performance mark, keyed by appName.", - "high": 5000, - "n_buckets": 10 - }, - "DEVTOOLS_HUD_APP_STARTUP_TIME_FULLYLOADED": { - "alert_emails": ["rnicoletti@mozilla.com","thills@mozilla.com"], - "expires_in_version": "52", - "kind": "linear", - "keyed": true, - "description": "The duration in ms between application launch and the 'fullyLoaded' performance mark, keyed by appName.", - "high": 30000, - "n_buckets": 30 - }, - "DEVTOOLS_HUD_APP_STARTUP_TIME_SCANEND": { - "alert_emails": ["rnicoletti@mozilla.com","thills@mozilla.com"], - "expires_in_version": "52", - "kind": "linear", - "keyed": true, - "description": "The duration in ms between application launch and the 'scanEnd' performance mark, keyed by appName.", - "high": 30000, - "n_buckets": 30 - }, - "DEVTOOLS_HUD_APP_MEMORY_CONTENTINTERACTIVE_V2": { - "alert_emails": ["rnicoletti@mozilla.com","thills@mozilla.com"], - "expires_in_version": "52", - "kind": "linear", - "keyed": true, - "description": "The USS memory consumed by an application at the time of the 'contentInteractive' performance mark, keyed by appName.", - "low": 20000000, - "high": 30000000, - "n_buckets": 10 - }, - "DEVTOOLS_HUD_APP_MEMORY_NAVIGATIONINTERACTIVE_V2": { - "alert_emails": ["rnicoletti@mozilla.com","thills@mozilla.com"], - "expires_in_version": "52", - "kind": "linear", - "keyed": true, - "description": "The USS memory consumed by an application at the time of the 'navigationInteractive' performance mark, keyed by appName.", - "low": 20000000, - "high": 30000000, - "n_buckets": 10 - }, - "DEVTOOLS_HUD_APP_MEMORY_NAVIGATIONLOADED_V2": { - "alert_emails": ["rnicoletti@mozilla.com","thills@mozilla.com"], - "expires_in_version": "52", - "kind": "linear", - "keyed": true, - "description": "The USS memory consumed by an application at the time of the 'navigationLoaded' performance mark, keyed by appName.", - "low": 20000000, - "high": 30000000, - "n_buckets": 10 - }, - "DEVTOOLS_HUD_APP_MEMORY_VISUALLYLOADED_V2": { - "alert_emails": ["rnicoletti@mozilla.com","thills@mozilla.com"], - "expires_in_version": "52", - "kind": "linear", - "keyed": true, - "description": "The USS memory consumed by an application at the time of the 'visuallyLoaded' performance mark, keyed by appName.", - "low": 20000000, - "high": 30000000, - "n_buckets": 10 - }, - "DEVTOOLS_HUD_APP_MEMORY_MEDIAENUMERATED_V2": { - "alert_emails": ["rnicoletti@mozilla.com","thills@mozilla.com"], - "expires_in_version": "52", - "kind": "linear", - "keyed": true, - "description": "The USS memory consumed by an application at the time of the 'mediaEnumerated' performance mark, keyed by appName.", - "low": 20000000, - "high": 40000000, - "n_buckets": 10 - }, - "DEVTOOLS_HUD_APP_MEMORY_FULLYLOADED_V2": { - "alert_emails": ["rnicoletti@mozilla.com","thills@mozilla.com"], - "expires_in_version": "52", - "kind": "linear", - "keyed": true, - "description": "The USS memory consumed by an application at the time of the 'fullyLoaded' performance mark, keyed by appName.", - "low": 20000000, - "high": 40000000, - "n_buckets": 20 - }, - "DEVTOOLS_HUD_APP_MEMORY_SCANEND_V2": { - "alert_emails": ["rnicoletti@mozilla.com","thills@mozilla.com"], - "expires_in_version": "52", - "kind": "linear", - "keyed": true, - "description": "The USS memory consumed by an application at the time of the 'scanEnd' performance mark, keyed by appName.", - "low": 20000000, - "high": 40000000, - "n_buckets": 20 - }, "DEVTOOLS_MEMORY_TAKE_SNAPSHOT_COUNT": { "expires_in_version": "56", "kind": "count", diff --git a/toolkit/components/telemetry/histogram-whitelists.json b/toolkit/components/telemetry/histogram-whitelists.json index 1492e4eb62bf..b65a97bdef0e 100644 --- a/toolkit/components/telemetry/histogram-whitelists.json +++ b/toolkit/components/telemetry/histogram-whitelists.json @@ -893,27 +893,6 @@ "DEVTOOLS_FONTINSPECTOR_TIME_ACTIVE_SECONDS", "DEVTOOLS_HEAP_SNAPSHOT_EDGE_COUNT", "DEVTOOLS_HEAP_SNAPSHOT_NODE_COUNT", - "DEVTOOLS_HUD_APP_MEMORY_CONTENTINTERACTIVE_V2", - "DEVTOOLS_HUD_APP_MEMORY_FULLYLOADED_V2", - "DEVTOOLS_HUD_APP_MEMORY_MEDIAENUMERATED_V2", - "DEVTOOLS_HUD_APP_MEMORY_NAVIGATIONINTERACTIVE_V2", - "DEVTOOLS_HUD_APP_MEMORY_NAVIGATIONLOADED_V2", - "DEVTOOLS_HUD_APP_MEMORY_SCANEND_V2", - "DEVTOOLS_HUD_APP_MEMORY_VISUALLYLOADED_V2", - "DEVTOOLS_HUD_APP_STARTUP_TIME_CONTENTINTERACTIVE", - "DEVTOOLS_HUD_APP_STARTUP_TIME_FULLYLOADED", - "DEVTOOLS_HUD_APP_STARTUP_TIME_MEDIAENUMERATED", - "DEVTOOLS_HUD_APP_STARTUP_TIME_NAVIGATIONINTERACTIVE", - "DEVTOOLS_HUD_APP_STARTUP_TIME_NAVIGATIONLOADED", - "DEVTOOLS_HUD_APP_STARTUP_TIME_SCANEND", - "DEVTOOLS_HUD_APP_STARTUP_TIME_VISUALLYLOADED", - "DEVTOOLS_HUD_ERRORS", - "DEVTOOLS_HUD_JANK", - "DEVTOOLS_HUD_REFLOWS", - "DEVTOOLS_HUD_REFLOW_DURATION", - "DEVTOOLS_HUD_SECURITY_CATEGORY", - "DEVTOOLS_HUD_USS", - "DEVTOOLS_HUD_WARNINGS", "DEVTOOLS_INSPECTOR_TIME_ACTIVE_SECONDS", "DEVTOOLS_JSBROWSERDEBUGGER_TIME_ACTIVE_SECONDS", "DEVTOOLS_JSDEBUGGER_TIME_ACTIVE_SECONDS", diff --git a/toolkit/content/widgets/browser.xml b/toolkit/content/widgets/browser.xml index 0120f046dfa3..ed0a269cef21 100644 --- a/toolkit/content/widgets/browser.xml +++ b/toolkit/content/widgets/browser.xml @@ -1281,16 +1281,16 @@ // The request comes from a XPCOM component, we'd want to redirect // the request to tabbrowser so tabbrowser will be setup correctly, // and it will eventually call swapDocShells. - let tabbrowser = this.getTabBrowser(); - if (tabbrowser) { - let tab = tabbrowser.getTabForBrowser(this); - if (tab) { - tabbrowser.swapBrowsers(tab, aOtherBrowser, aFlags); - return; - } + let ourTabBrowser = this.getTabBrowser(); + let otherTabBrowser = aOtherBrowser.getTabBrowser(); + if (ourTabBrowser && otherTabBrowser) { + let ourTab = ourTabBrowser.getTabForBrowser(this); + let otherTab = otherTabBrowser.getTabForBrowser(aOtherBrowser); + ourTabBrowser.swapBrowsers(ourTab, otherTab, aFlags); + return; } - // If we're not attached to a tabbrowser, just swap. + // One of us is not connected to a tabbrowser, so just swap. this.swapDocShells(aOtherBrowser); ]]> diff --git a/xpfe/appshell/nsIXULBrowserWindow.idl b/xpfe/appshell/nsIXULBrowserWindow.idl index 6c5d4c5422fe..155634b1038f 100644 --- a/xpfe/appshell/nsIXULBrowserWindow.idl +++ b/xpfe/appshell/nsIXULBrowserWindow.idl @@ -8,6 +8,7 @@ #include "nsIURI.idl" #include "nsIDOMNode.idl" +interface nsIBrowser; interface nsIRequest; interface nsIDOMElement; interface nsIInputStream; @@ -73,5 +74,14 @@ interface nsIXULBrowserWindow : nsISupports * Return the number of tabs in this window. */ uint32_t getTabCount(); + + /** + * Navigate the browser to the given history index after restoring the full history + * from SessionStore. If the browser is currently in GroupedSHistory mode, it will + * be reverted to a non-grouped history mode. If a process change is required to + * perform the load, this will also occur. + */ + void navigateAndRestoreByIndex(in nsIBrowser aBrowser, in long aIndex); + };