author | Sebastian Hengst <archaeopteryx@coole-files.de> |
Mon, 25 Sep 2017 11:56:13 +0200 | |
changeset 382684 | 13ce77b78e364cc952d51b959f22202502be2941 |
parent 382651 | 7e962631ba4298bcefa571008661983d77c3e652 (current diff) |
parent 382683 | f01e65b7038feefbc93f198016c3c82ba5c02fb3 (diff) |
child 382685 | 5f3f19824efa14cc6db546baf59c54a0fc15ddc9 |
push id | 95394 |
push user | archaeopteryx@coole-files.de |
push date | Mon, 25 Sep 2017 10:03:57 +0000 |
treeherder | mozilla-inbound@ff48fab67d3c [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge, merge |
milestone | 58.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
dom/indexedDB/test/bfcache_iframe1.html | file | annotate | diff | comparison | revisions | |
dom/indexedDB/test/bfcache_iframe2.html | file | annotate | diff | comparison | revisions | |
dom/media/webspeech/synth/test/file_bfcache_frame.html | file | annotate | diff | comparison | revisions | |
dom/media/webspeech/synth/test/file_bfcache_frame2.html | file | annotate | diff | comparison | revisions | |
dom/workers/test/WorkerDebugger_frozen_iframe1.html | file | annotate | diff | comparison | revisions | |
dom/workers/test/WorkerDebugger_frozen_iframe2.html | file | annotate | diff | comparison | revisions | |
dom/workers/test/suspend_iframe.html | file | annotate | diff | comparison | revisions | |
layout/painting/nsDisplayList.cpp | file | annotate | diff | comparison | revisions |
--- a/browser/components/customizableui/CustomizeMode.jsm +++ b/browser/components/customizableui/CustomizeMode.jsm @@ -2271,16 +2271,21 @@ CustomizeMode.prototype = { while (aReferenceNode && aReferenceNode.localName == "toolbarpaletteitem" && aReferenceNode.firstChild.hidden) { aReferenceNode = aReferenceNode.previousSibling; } return aReferenceNode; }, + onPaletteContextMenuShowing(event) { + let isFlexibleSpace = event.target.triggerNode.id.includes("wrapper-customizableui-special-spring"); + event.target.querySelector(".customize-context-addToPanel").disabled = isFlexibleSpace; + }, + onPanelContextMenuShowing(event) { let inPermanentArea = !!event.target.triggerNode.closest("#widget-overflow-fixed-list"); let doc = event.target.ownerDocument; doc.getElementById("customizationPanelItemContextMenuUnpin").hidden = !inPermanentArea; doc.getElementById("customizationPanelItemContextMenuPin").hidden = inPermanentArea; }, _checkForDownloadsClick(event) {
--- a/browser/components/customizableui/content/panelUI.inc.xul +++ b/browser/components/customizableui/content/panelUI.inc.xul @@ -457,17 +457,18 @@ buttonhighlight="true" hidden="true"> <popupnotificationcontent id="update-restart-notification-content" orient="vertical"> <description id="update-restart-description">&updateRestart.message2;</description> </popupnotificationcontent> </popupnotification> </panel> -<menupopup id="customizationPaletteItemContextMenu"> +<menupopup id="customizationPaletteItemContextMenu" + onpopupshowing="gCustomizeMode.onPaletteContextMenuShowing(event)"> <menuitem oncommand="gCustomizeMode.addToToolbar(document.popupNode)" class="customize-context-addToToolbar" accesskey="&customizeMenu.addToToolbar.accesskey;" label="&customizeMenu.addToToolbar.label;"/> <menuitem oncommand="gCustomizeMode.addToPanel(document.popupNode)" class="customize-context-addToPanel" accesskey="&customizeMenu.addToOverflowMenu.accesskey;" label="&customizeMenu.addToOverflowMenu.label;"/>
--- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -8319,26 +8319,21 @@ nsDocShell::CanSavePresentation(uint32_t } // Avoid doing the work of saving the presentation state in the case where // the content viewer cache is disabled. if (nsSHistory::GetMaxTotalViewers() == 0) { return false; } - // Don't cache the content viewer if we're in a subframe and the subframe - // pref is disabled. - bool cacheFrames = - Preferences::GetBool("browser.sessionhistory.cache_subframes", false); - if (!cacheFrames) { - nsCOMPtr<nsIDocShellTreeItem> root; - GetSameTypeParent(getter_AddRefs(root)); - if (root && root != this) { - return false; // this is a subframe load - } + // Don't cache the content viewer if we're in a subframe. + nsCOMPtr<nsIDocShellTreeItem> root; + GetSameTypeParent(getter_AddRefs(root)); + if (root && root != this) { + return false; // this is a subframe load } // If the document does not want its presentation cached, then don't. nsCOMPtr<nsIDocument> doc = mScriptGlobal->GetExtantDoc(); return doc && doc->CanSavePresentation(aNewRequest); } void
--- a/docshell/shistory/nsISHistoryInternal.idl +++ b/docshell/shistory/nsISHistoryInternal.idl @@ -116,14 +116,28 @@ interface nsISHistoryInternal: nsISuppor * The container to start looking for dynamic entries. Only the * dynamic descendants of the container will be removed. If not given, * all dynamic entries at the index will be removed. */ [noscript, notxpcom] void RemoveDynEntries(in long aIndex, in nsISHContainer aContainer); /** + * Similar to RemoveDynEntries, but instead of specifying an index, use the + * given BFCacheEntry to find the index and remove dynamic entries from the + * index. + * + * The method takes no effect if the bfcache entry is not or no longer hold + * by the SHistory instance. + * + * @param aEntry + * The bfcache entry to look up for index to remove dynamic entries + * from. + */ + [noscript, notxpcom] void RemoveDynEntriesForBFCacheEntry(in nsIBFCacheEntry aEntry); + + /** * Removes entries from the history if their docshellID is in * aIDs array. */ [noscript, notxpcom] void RemoveEntries(in nsDocshellIDArray aIDs, in long aStartIndex); };
--- a/docshell/shistory/nsSHEntryShared.cpp +++ b/docshell/shistory/nsSHEntryShared.cpp @@ -45,17 +45,28 @@ nsSHEntryShared::nsSHEntryShared() , mSticky(true) , mDynamicallyCreated(false) , mExpired(false) { } nsSHEntryShared::~nsSHEntryShared() { + // The destruction can be caused by either the entry is removed from session + // history and no one holds the reference, or the whole session history is on + // destruction. We want to ensure that we invoke + // shistory->RemoveFromExpirationTracker for the former case. RemoveFromExpirationTracker(); + + // Calling RemoveDynEntriesForBFCacheEntry on destruction is unnecessary since + // there couldn't be any SHEntry holding this shared entry, and we noticed + // that calling RemoveDynEntriesForBFCacheEntry in the middle of + // nsSHistory::Release can cause a crash, so set mSHistory to null explicitly + // before RemoveFromBFCacheSync. + mSHistory = nullptr; if (mContentViewer) { RemoveFromBFCacheSync(); } } NS_IMPL_ISUPPORTS(nsSHEntryShared, nsIBFCacheEntry, nsIMutationObserver) already_AddRefed<nsSHEntryShared> @@ -163,76 +174,76 @@ nsSHEntryShared::SetContentViewer(nsICon return NS_OK; } nsresult nsSHEntryShared::RemoveFromBFCacheSync() { MOZ_ASSERT(mContentViewer && mDocument, "we're not in the bfcache!"); + // The call to DropPresentationState could drop the last reference, so hold + // |this| until RemoveDynEntriesForBFCacheEntry finishes. + RefPtr<nsSHEntryShared> kungFuDeathGrip = this; + + // DropPresentationState would clear mContentViewer. nsCOMPtr<nsIContentViewer> viewer = mContentViewer; DropPresentationState(); - // Warning! The call to DropPresentationState could have dropped the last - // reference to this object, so don't access members beyond here. - if (viewer) { viewer->Destroy(); } + // Now that we've dropped the viewer, we have to clear associated dynamic + // subframe entries. + nsCOMPtr<nsISHistoryInternal> shistory = do_QueryReferent(mSHistory); + if (shistory) { + shistory->RemoveDynEntriesForBFCacheEntry(this); + } + return NS_OK; } -class DestroyViewerEvent : public mozilla::Runnable -{ -public: - DestroyViewerEvent(nsIContentViewer* aViewer, nsIDocument* aDocument) - : mozilla::Runnable("DestroyViewerEvent") - , mViewer(aViewer) - , mDocument(aDocument) - { - } - - NS_IMETHOD Run() override - { - if (mViewer) { - mViewer->Destroy(); - } - return NS_OK; - } - - nsCOMPtr<nsIContentViewer> mViewer; - nsCOMPtr<nsIDocument> mDocument; -}; - nsresult nsSHEntryShared::RemoveFromBFCacheAsync() { MOZ_ASSERT(mContentViewer && mDocument, "we're not in the bfcache!"); - // Release the reference to the contentviewer asynchronously so that the - // document doesn't get nuked mid-mutation. - + // Check it again to play safe in release builds. if (!mDocument) { return NS_ERROR_UNEXPECTED; } - nsCOMPtr<nsIRunnable> evt = new DestroyViewerEvent(mContentViewer, mDocument); - nsresult rv = mDocument->Dispatch(mozilla::TaskCategory::Other, evt.forget()); + + // DropPresentationState would clear mContentViewer & mDocument. Capture and + // release the references asynchronously so that the document doesn't get + // nuked mid-mutation. + nsCOMPtr<nsIContentViewer> viewer = mContentViewer; + nsCOMPtr<nsIDocument> document = mDocument; + RefPtr<nsSHEntryShared> self = this; + nsresult rv = mDocument->Dispatch(mozilla::TaskCategory::Other, + NS_NewRunnableFunction("nsSHEntryShared::RemoveFromBFCacheAsync", + [self, viewer, document]() { + if (viewer) { + viewer->Destroy(); + } + + nsCOMPtr<nsISHistoryInternal> shistory = do_QueryReferent(self->mSHistory); + if (shistory) { + shistory->RemoveDynEntriesForBFCacheEntry(self); + } + })); + if (NS_FAILED(rv)) { - NS_WARNING("failed to dispatch DestroyViewerEvent"); + NS_WARNING("Failed to dispatch RemoveFromBFCacheAsync runnable."); } else { // Drop presentation. Only do this if we succeeded in posting the event // since otherwise the document could be torn down mid-mutation, causing // crashes. DropPresentationState(); } - // Careful! The call to DropPresentationState could have dropped the last - // reference to this nsSHEntryShared, so don't access members beyond here. - return NS_OK; } nsresult nsSHEntryShared::GetID(uint64_t* aID) { *aID = mID; return NS_OK;
--- a/docshell/shistory/nsSHistory.cpp +++ b/docshell/shistory/nsSHistory.cpp @@ -208,17 +208,17 @@ nsSHistory::EvictContentViewerForTransac entry->GetAnyContentViewer(getter_AddRefs(ownerEntry), getter_AddRefs(viewer)); if (viewer) { NS_ASSERTION(ownerEntry, "Content viewer exists but its SHEntry is null"); LOG_SHENTRY_SPEC(("Evicting content viewer 0x%p for " "owning SHEntry 0x%p at %s.", viewer.get(), ownerEntry.get(), _spec), - ownerEntry); + ownerEntry); // Drop the presentation state before destroying the viewer, so that // document teardown is able to correctly persist the state. ownerEntry->SetContentViewer(nullptr); ownerEntry->SyncPresentationState(); viewer->Destroy(); } @@ -583,17 +583,17 @@ nsSHistory::GetEntryAtIndex(int32_t aInd NOTIFY_LISTENERS(OnIndexChanged, (mIndex)) } } } return rv; } /* Get the transaction at a given index */ -NS_IMETHODIMP +nsresult nsSHistory::GetTransactionAtIndex(int32_t aIndex, nsISHTransaction** aResult) { nsresult rv; NS_ENSURE_ARG_POINTER(aResult); if (mLength <= 0 || aIndex < 0 || aIndex >= mLength) { return NS_ERROR_FAILURE; } @@ -1285,18 +1285,23 @@ nsSHistory::GloballyEvictContentViewers( for (int32_t i = transactions.Length() - 1; i >= sHistoryMaxTotalViewers; --i) { (transactions[i].mSHistory)-> EvictContentViewerForTransaction(transactions[i].mTransaction); } } nsresult -nsSHistory::EvictExpiredContentViewerForEntry(nsIBFCacheEntry* aEntry) +nsSHistory::FindTransactionForBFCache(nsIBFCacheEntry* aEntry, + nsISHTransaction** aResult, + int32_t* aResultIndex) { + *aResult = nullptr; + *aResultIndex = -1; + int32_t startIndex = std::max(0, mIndex - nsISHistory::VIEWER_WINDOW); int32_t endIndex = std::min(mLength - 1, mIndex + nsISHistory::VIEWER_WINDOW); nsCOMPtr<nsISHTransaction> trans; GetTransactionAtIndex(startIndex, getter_AddRefs(trans)); int32_t i; for (i = startIndex; trans && i <= endIndex; ++i) { nsCOMPtr<nsISHEntry> entry; @@ -1306,25 +1311,39 @@ nsSHistory::EvictExpiredContentViewerFor if (entry->HasBFCacheEntry(aEntry)) { break; } nsCOMPtr<nsISHTransaction> temp = trans; temp->GetNext(getter_AddRefs(trans)); } if (i > endIndex) { - return NS_OK; + return NS_ERROR_FAILURE; } - if (i == mIndex) { + trans.forget(aResult); + *aResultIndex = i; + return NS_OK; +} + +nsresult +nsSHistory::EvictExpiredContentViewerForEntry(nsIBFCacheEntry* aEntry) +{ + int32_t index; + nsCOMPtr<nsISHTransaction> trans; + FindTransactionForBFCache(aEntry, getter_AddRefs(trans), &index); + + if (index == mIndex) { NS_WARNING("How did the current SHEntry expire?"); return NS_OK; } - EvictContentViewerForTransaction(trans); + if (trans) { + EvictContentViewerForTransaction(trans); + } return NS_OK; } NS_IMETHODIMP nsSHistory::AddToExpirationTracker(nsIBFCacheEntry* aEntry) { RefPtr<nsSHEntryShared> entry = static_cast<nsSHEntryShared*>(aEntry); @@ -1581,16 +1600,30 @@ nsSHistory::RemoveDynEntries(int32_t aIn AutoTArray<nsID, 16> toBeRemovedEntries; GetDynamicChildren(container, toBeRemovedEntries, true); if (toBeRemovedEntries.Length()) { RemoveEntries(toBeRemovedEntries, aIndex); } } } +void +nsSHistory::RemoveDynEntriesForBFCacheEntry(nsIBFCacheEntry* aEntry) +{ + int32_t index; + nsCOMPtr<nsISHTransaction> trans; + FindTransactionForBFCache(aEntry, getter_AddRefs(trans), &index); + if (trans) { + nsCOMPtr<nsISHEntry> entry; + trans->GetSHEntry(getter_AddRefs(entry)); + nsCOMPtr<nsISHContainer> container(do_QueryInterface(entry)); + RemoveDynEntries(index, container); + } +} + NS_IMETHODIMP nsSHistory::UpdateIndex() { // Update the actual index with the right value. if (mIndex != mRequestedIndex && mRequestedIndex != -1) { mIndex = mRequestedIndex; NOTIFY_LISTENERS(OnIndexChanged, (mIndex)) } @@ -1698,17 +1731,17 @@ nsSHistory::LoadNextPossibleEntry(int32_ return LoadEntry(aNewIndex - 1, aLoadType, aHistCmd); } if (aNewIndex > mIndex) { return LoadEntry(aNewIndex + 1, aLoadType, aHistCmd); } return NS_ERROR_FAILURE; } -NS_IMETHODIMP +nsresult nsSHistory::LoadEntry(int32_t aIndex, long aLoadType, uint32_t aHistCmd) { if (!mRootDocShell) { return NS_ERROR_FAILURE; } nsCOMPtr<nsIURI> nextURI; nsCOMPtr<nsISHEntry> prevEntry;
--- a/docshell/shistory/nsSHistory.h +++ b/docshell/shistory/nsSHistory.h @@ -78,30 +78,35 @@ public: // Otherwise, it comes straight from the pref. static uint32_t GetMaxTotalViewers() { return sHistoryMaxTotalViewers; } private: virtual ~nsSHistory(); friend class nsSHEnumerator; friend class nsSHistoryObserver; - // Could become part of nsIWebNavigation - NS_IMETHOD GetTransactionAtIndex(int32_t aIndex, nsISHTransaction** aResult); + nsresult GetTransactionAtIndex(int32_t aIndex, nsISHTransaction** aResult); nsresult LoadDifferingEntries(nsISHEntry* aPrevEntry, nsISHEntry* aNextEntry, nsIDocShell* aRootDocShell, long aLoadType, bool& aDifferenceFound); nsresult InitiateLoad(nsISHEntry* aFrameEntry, nsIDocShell* aFrameDS, long aLoadType); - NS_IMETHOD LoadEntry(int32_t aIndex, long aLoadType, uint32_t aHistCmd); + nsresult LoadEntry(int32_t aIndex, long aLoadType, uint32_t aHistCmd); #ifdef DEBUG nsresult PrintHistory(); #endif + // Find the transaction for a given bfcache entry. It only looks up between + // the range where alive viewers may exist (i.e nsISHistory::VIEWER_WINDOW). + nsresult FindTransactionForBFCache(nsIBFCacheEntry* aEntry, + nsISHTransaction** aResult, + int32_t* aResultIndex); + // Evict content viewers in this window which don't lie in the "safe" range // around aIndex. void EvictOutOfRangeWindowContentViewers(int32_t aIndex); void EvictContentViewerForTransaction(nsISHTransaction* aTrans); static void GloballyEvictContentViewers(); static void GloballyEvictAllContentViewers(); // Calculates a max number of total
--- a/docshell/test/chrome/bug608669.xul +++ b/docshell/test/chrome/bug608669.xul @@ -1,6 +1,14 @@ <?xml version="1.0"?> <?xml-stylesheet href="chrome://global/skin" type="text/css"?> <window title="Mozilla Bug 608669 - Blank page" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + onload="notifyOpener();"> <description flex="1" value="This window is intentionally left blank"/> + <script type="application/javascript"> + function notifyOpener() { + if (opener) { + opener.postMessage("load", "*"); + } + } + </script> </window>
--- a/docshell/test/chrome/test_bug608669.xul +++ b/docshell/test/chrome/test_bug608669.xul @@ -13,53 +13,28 @@ https://bugzilla.mozilla.org/show_bug.cg <body xmlns="http://www.w3.org/1999/xhtml"> <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=608669" target="_blank">Mozilla Bug 608669</a> </body> <!-- test code goes here --> <script type="application/javascript"><![CDATA[ -var gOrigMaxTotalViewers = undefined; -function setCachePref(enabled) { - var prefBranch = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); - if (enabled) { - is(typeof gOrigMaxTotalViewers, "undefined", "don't double-enable bfcache"); - prefBranch.setBoolPref("browser.sessionhistory.cache_subframes", true); - gOrigMaxTotalViewers = prefBranch.getIntPref("browser.sessionhistory.max_total_viewers"); - prefBranch.setIntPref("browser.sessionhistory.max_total_viewers", 10); - } - else { - is(typeof gOrigMaxTotalViewers, "number", "don't double-disable bfcache"); - prefBranch.setIntPref("browser.sessionhistory.max_total_viewers", gOrigMaxTotalViewers); - gOrigMaxTotalViewers = undefined; - try { - prefBranch.clearUserPref("browser.sessionhistory.cache_subframes"); - } catch (e) { /* Pref didn't exist, meh */ } - } -} - - /** Test for Bug 608669 **/ SimpleTest.waitForExplicitFinish(); addLoadEvent(nextTest); gen = doTest(); function nextTest() { gen.next(); } function* doTest() { - var container = document.getElementById('container'); - - setCachePref(true); - var notificationCount = 0; var observer = { observe: function(aSubject, aTopic, aData) { is(aTopic, "chrome-document-global-created", "correct topic"); is(aData, "null", "correct data"); notificationCount++; @@ -68,49 +43,36 @@ function* doTest() { var os = Components.classes["@mozilla.org/observer-service;1"]. getService(Components.interfaces.nsIObserverService); os.addObserver(observer, "chrome-document-global-created"); os.addObserver(observer, "content-document-global-created"); is(notificationCount, 0, "initial count"); - // create a new iframe - var iframe = document.createElement("iframe"); - container.appendChild(iframe); - iframe.contentWindow.x = "y"; - is(notificationCount, 1, "after created iframe"); - - // Try loading in an iframe - iframe.setAttribute("src", "bug608669.xul"); - iframe.onload = nextTest; + // create a new window + var testWin = window.open("", "bug 608669", "chrome,width=600,height=600"); + testWin.x = "y"; + is(notificationCount, 1, "after created window"); + + // Try loading in the window + testWin.location = "bug608669.xul"; + window.onmessage = nextTest; yield undefined; is(notificationCount, 1, "after first load"); - is(iframe.contentWindow.x, "y", "reused window"); + is(testWin.x, "y", "reused window"); - // Try loading again in an iframe - iframe.setAttribute("src", "bug608669.xul?x"); - iframe.onload = nextTest; + // Try loading again in the window + testWin.location = "bug608669.xul?x"; + window.onmessage = nextTest; yield undefined; is(notificationCount, 2, "after second load"); - is("x" in iframe.contentWindow, false, "didn't reuse window"); + is("x" in testWin, false, "didn't reuse window"); - // Open a new window using window.open - popup = window.open("bug608669.xul", "bug 608669", - "chrome,width=600,height=600"); - popup.onload = nextTest; - yield undefined; - is(notificationCount, 3, "after window.open load"); - popup.close(); + testWin.close(); - setCachePref(false); os.removeObserver(observer, "chrome-document-global-created"); os.removeObserver(observer, "content-document-global-created"); SimpleTest.finish(); } - - ]]></script> - <vbox id="container" flex="1"> - <description>Below will an iframe be added</description> - </vbox> </window>
new file mode 100644 --- /dev/null +++ b/docshell/test/navigation/file_bug1364364-1.html @@ -0,0 +1,33 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>title</title> + </head> + <body onload="loadFramesAndNavigate();"> + <p id="content"></p> + <div id="frameContainer"> + </div> + <script type="application/javascript"> + function waitForLoad(frame) { + return new Promise(r => frame.onload = r); + } + + async function loadFramesAndNavigate() { + let dynamicFrame = document.createElement("iframe"); + dynamicFrame.src = "data:text/html,iframe1"; + document.querySelector("#frameContainer").appendChild(dynamicFrame); + await waitForLoad(dynamicFrame); + dynamicFrame.src = "data:text/html,iframe2"; + await waitForLoad(dynamicFrame); + dynamicFrame.src = "data:text/html,iframe3"; + await waitForLoad(dynamicFrame); + dynamicFrame.src = "data:text/html,iframe4"; + await waitForLoad(dynamicFrame); + dynamicFrame.src = "data:text/html,iframe5"; + await waitForLoad(dynamicFrame); + location.href = "file_bug1364364-2.html"; + } + </script> + </body> +</html>
new file mode 100644 --- /dev/null +++ b/docshell/test/navigation/file_bug1364364-2.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>title</title> + </head> + <body onload="notifyOpener();"> + <script type="application/javascript"> + function notifyOpener() { + opener.postMessage("navigation-done", "*"); + } + </script> + </body> +</html>
--- a/docshell/test/navigation/mochitest.ini +++ b/docshell/test/navigation/mochitest.ini @@ -44,30 +44,33 @@ support-files = file_triggeringprincipal_iframe_iframe_window_open_frame_a_nav.html file_bug1300461.html file_bug1300461_redirect.html file_bug1300461_redirect.html^headers^ file_bug1300461_back.html file_contentpolicy_block_window.html file_bug1326251.html file_bug1326251_evict_cache.html + file_bug1364364-1.html + file_bug1364364-2.html file_bug1375833.html file_bug1375833-frame1.html file_bug1375833-frame2.html [test_bug13871.html] [test_bug270414.html] [test_bug278916.html] [test_bug279495.html] [test_bug344861.html] skip-if = toolkit == "android" || toolkit == "windows" # disabled on Windows because of bug 1234520 [test_bug386782.html] [test_bug430624.html] [test_bug430723.html] skip-if = (toolkit == 'android') || (!debug && (os == 'mac' || os == 'win')) # Bug 874423 +[test_bug1364364.html] [test_bug1375833.html] [test_child.html] [test_grandchild.html] [test_not-opener.html] [test_opener.html] [test_popup-navigates-children.html] [test_reserved.html] skip-if = (toolkit == 'android') || (debug && e10s) #too slow on Android 4.3 aws only; bug 1030403; bug 1263213 for debug e10s
new file mode 100644 --- /dev/null +++ b/docshell/test/navigation/test_bug1364364.html @@ -0,0 +1,56 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1364364 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1364364</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript"> + + /** Test for Bug 1364364 **/ + let testWin, testDoc; + async function test() { + SimpleTest.waitForExplicitFinish(); + testWin = window.open("file_bug1364364-1.html"); + await waitForLoad(testWin); + testDoc = testWin.document; + + // file_bug1364364-1.html will load a few dynamic iframes and then navigate + // top browsing context to file_bug1364364-2.html, which will postMessage + // back. + } + + function waitForLoad(win) { + return new Promise(r => win.addEventListener("load", r, { once: true})); + } + + window.addEventListener("message", async function(msg) { + if (msg.data == "navigation-done") { + is(testWin.history.length, 6, "check history.length"); + + // Modify a document in bfcache should cause the cache being dropped tho + // RemoveFromBFCacheAsync. + testDoc.querySelector("#content").textContent = "modified"; + await new Promise(r => setTimeout(r, 0)); + + is(testWin.history.length, 2, "check history.length after bfcache dropped"); + testWin.close(); + SimpleTest.finish(); + } + }); + + </script> +</head> +<body onload="test();"> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1364364">Mozilla Bug 1364364</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html>
--- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -1893,16 +1893,29 @@ Element::UnbindFromTree(bool aDeep, bool DeleteProperty(nsGkAtoms::transitionsOfBeforeProperty); DeleteProperty(nsGkAtoms::transitionsOfAfterProperty); DeleteProperty(nsGkAtoms::transitionsProperty); DeleteProperty(nsGkAtoms::animationsOfBeforeProperty); DeleteProperty(nsGkAtoms::animationsOfAfterProperty); DeleteProperty(nsGkAtoms::animationsProperty); } + // Computed style data isn't useful for detached nodes, and we'll need to + // recompute it anyway if we ever insert the nodes back into a document. + if (IsStyledByServo()) { + if (document) { + ClearServoData(document); + } else { + MOZ_ASSERT(!HasServoData()); + MOZ_ASSERT(!HasAnyOfFlags(kAllServoDescendantBits | NODE_NEEDS_FRAME)); + } + } else { + MOZ_ASSERT(!HasServoData()); + } + // Editable descendant count only counts descendants that // are in the uncomposed document. ResetEditableDescendantCount(); if (aNullParent || !mParent->IsInShadowTree()) { UnsetFlags(NODE_IS_IN_SHADOW_TREE); // Begin keeping track of our subtree root. @@ -1987,28 +2000,31 @@ Element::UnbindFromTree(bool aDeep, bool for (nsIContent* child = shadowRoot->GetFirstChild(); child; child = child->GetNextSibling()) { child->UnbindFromTree(true, false); } shadowRoot->SetIsComposedDocParticipant(false); } - // Computed style data isn't useful for detached nodes, and we'll need to - // recompute it anyway if we ever insert the nodes back into a document. - if (IsStyledByServo()) { - if (document) { - ClearServoData(document); - } else { - MOZ_ASSERT(!HasServoData()); - MOZ_ASSERT(!HasAnyOfFlags(kAllServoDescendantBits | NODE_NEEDS_FRAME)); - } - } else { - MOZ_ASSERT(!HasServoData()); - } + // Unbinding of children is the only point in time where we don't enforce the + // "child has style data implies parent has it too" invariant. + // + // As such, the restyle root tracking may incorrectly end up setting dirty + // bits on the parent chain when moving from a not yet unbound root with + // already unbound parents to a root higher up in the tree, so we clear those + // (again, since they're also cleared in ClearServoData) here. + // + // This can happen when the element changes the state of some ancestor up in + // the tree, for example. + // + // Note that clearing the data itself here would have its own set of problems, + // since the invariant we'd be breaking in that case is "HasServoData() + // implies InComposedDoc()", which we rely on in various places. + UnsetFlags(kAllServoDescendantBits); } nsICSSDeclaration* Element::GetSMILOverrideStyle() { Element::nsExtendedDOMSlots* slots = ExtendedDOMSlots(); if (!slots->mSMILOverrideStyle) {
rename from dom/indexedDB/test/bfcache_iframe1.html rename to dom/indexedDB/test/bfcache_page1.html --- a/dom/indexedDB/test/bfcache_iframe1.html +++ b/dom/indexedDB/test/bfcache_page1.html @@ -1,27 +1,27 @@ <!DOCTYPE html> <html> <head> <script> - var request = indexedDB.open(parent.location, 1); + var request = indexedDB.open(opener.location, 1); request.onupgradeneeded = function(e) { var db = e.target.result; // This should never be called db.onversionchange = function(e) { db.transaction(["mystore"]).objectStore("mystore").put({ hello: "fail" }, 42); } var trans = e.target.transaction; if (db.objectStoreNames.contains("mystore")) { db.deleteObjectStore("mystore"); } var store = db.createObjectStore("mystore"); store.add({ hello: "world" }, 42); trans.oncomplete = function() { - parent.postMessage("go", "http://mochi.test:8888"); + opener.postMessage("go", "http://mochi.test:8888"); } }; </script> </head> <body> This is page one. </body> </html>
rename from dom/indexedDB/test/bfcache_iframe2.html rename to dom/indexedDB/test/bfcache_page2.html --- a/dom/indexedDB/test/bfcache_iframe2.html +++ b/dom/indexedDB/test/bfcache_page2.html @@ -1,28 +1,28 @@ <!DOCTYPE html> <html> <head> <script> var res = {}; - var request = indexedDB.open(parent.location, 2); + var request = indexedDB.open(opener.location, 2); request.onblocked = function() { res.blockedFired = true; } request.onupgradeneeded = function(e) { var db = e.target.result; res.version = db.version; res.storeCount = db.objectStoreNames.length; var trans = request.transaction; trans.objectStore("mystore").get(42).onsuccess = function(e) { res.value = JSON.stringify(e.target.result); } trans.oncomplete = function() { - parent.postMessage(JSON.stringify(res), "http://mochi.test:8888"); + opener.postMessage(JSON.stringify(res), "http://mochi.test:8888"); } }; </script> </head> <body> This is page two. </body>
--- a/dom/indexedDB/test/mochitest.ini +++ b/dom/indexedDB/test/mochitest.ini @@ -1,16 +1,16 @@ # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. [DEFAULT] support-files = - bfcache_iframe1.html - bfcache_iframe2.html + bfcache_page1.html + bfcache_page2.html blob_worker_crash_iframe.html error_events_abort_transactions_iframe.html event_propagation_iframe.html exceptions_in_events_iframe.html file.js helpers.js leaving_page_iframe.html third_party_iframe1.html
--- a/dom/indexedDB/test/test_bfcache.html +++ b/dom/indexedDB/test/test_bfcache.html @@ -5,63 +5,37 @@ <html> <head> <title>Indexed Database Property Test</title> <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> <script type="text/javascript"> /* import-globals-from helpers.js */ - var gOrigMaxTotalViewers = undefined; - function setCachePref(enabled) { - if (enabled) { - is(typeof gOrigMaxTotalViewers, "undefined", - "don't double-enable bfcache"); - SpecialPowers.setBoolPref("browser.sessionhistory.cache_subframes", - true); - gOrigMaxTotalViewers = - SpecialPowers.getIntPref("browser.sessionhistory.max_total_viewers"); - SpecialPowers.setIntPref("browser.sessionhistory.max_total_viewers", - 10); - } - else { - is(typeof gOrigMaxTotalViewers, "number", - "don't double-disable bfcache"); - SpecialPowers.setIntPref("browser.sessionhistory.max_total_viewers", - gOrigMaxTotalViewers); - gOrigMaxTotalViewers = undefined; - try { - SpecialPowers.clearUserPref("browser.sessionhistory.cache_subframes"); - } catch (e) { /* Pref didn't exist, meh */ } - } - } function* testSteps() { - var iframe = $("iframe"); - setCachePref(true); window.onmessage = grabEventAndContinueHandler; - iframe.src = "bfcache_iframe1.html"; + let testWin = window.open("bfcache_page1.html", "testWin"); var event = yield undefined; is(event.data, "go", "set up database successfully"); - iframe.src = "bfcache_iframe2.html"; + testWin.location = "bfcache_page2.html"; let res = JSON.parse((yield).data); is(res.version, 2, "version was set correctly"); is(res.storeCount, 1, "correct set of stores"); ok(!("blockedFired" in res), "blocked shouldn't fire"); is(res.value, JSON.stringify({ hello: "world" }), "correct value found in store"); - setCachePref(false); + testWin.close(); finishTest(); } </script> <script type="text/javascript" src="helpers.js"></script> </head> <body onload="runTest();"> - <iframe id="iframe"></iframe> </body> </html>
--- a/dom/media/platforms/wmf/WMFUtils.cpp +++ b/dom/media/platforms/wmf/WMFUtils.cpp @@ -9,18 +9,18 @@ #include "mozilla/ArrayUtils.h" #include "mozilla/CheckedInt.h" #include "mozilla/Logging.h" #include "mozilla/RefPtr.h" #include "nsTArray.h" #include "nsThreadUtils.h" #include "nsWindowsHelpers.h" #include "prenv.h" -#include <Shlobj.h> -#include <Shlwapi.h> +#include <shlobj.h> +#include <shlwapi.h> #include <initguid.h> #include <stdint.h> #include "mozilla/mscom/EnsureMTA.h" #include "mozilla/WindowsVersion.h" #ifdef WMF_MUST_DEFINE_AAC_MFT_CLSID // Some SDK versions don't define the AAC decoder CLSID. // {32D186A7-218F-4C75-8876-DD77273A8999}
rename from dom/media/webspeech/synth/test/file_bfcache_frame.html rename to dom/media/webspeech/synth/test/file_bfcache_page1.html --- a/dom/media/webspeech/synth/test/file_bfcache_frame.html +++ b/dom/media/webspeech/synth/test/file_bfcache_page1.html @@ -2,17 +2,17 @@ <html> <head> <meta charset="utf-8"> <script type="application/javascript"> addEventListener('pageshow', function onshow(evt) { var u = new SpeechSynthesisUtterance('hello'); u.lang = 'it-IT-noend'; u.addEventListener('start', function() { - location = "file_bfcache_frame2.html"; + location = "file_bfcache_page2.html"; }); speechSynthesis.speak(u); }); </script> </head> <body> </body> </html>
rename from dom/media/webspeech/synth/test/file_bfcache_frame2.html rename to dom/media/webspeech/synth/test/file_bfcache_page2.html --- a/dom/media/webspeech/synth/test/file_bfcache_frame2.html +++ b/dom/media/webspeech/synth/test/file_bfcache_page2.html @@ -1,14 +1,14 @@ <html> <script> var frameUnloaded = function() { var u = new SpeechSynthesisUtterance('hi'); u.addEventListener('end', function () { - parent.ok(true, 'Successfully spoke utterance from new frame.'); - parent.onDone(); + opener.ok(true, 'Successfully spoke utterance from new frame.'); + opener.onDone(); }); speechSynthesis.speak(u); }; </script> <body onpageshow="frameUnloaded()"></body></html>
--- a/dom/media/webspeech/synth/test/mochitest.ini +++ b/dom/media/webspeech/synth/test/mochitest.ini @@ -1,15 +1,15 @@ [DEFAULT] tags=msg subsuite = media support-files = common.js - file_bfcache_frame.html - file_bfcache_frame2.html + file_bfcache_page1.html + file_bfcache_page2.html file_setup.html file_speech_queue.html file_speech_simple.html file_speech_cancel.html file_speech_error.html file_indirect_service_events.html file_global_queue.html file_global_queue_cancel.html
--- a/dom/media/webspeech/synth/test/test_bfcache.html +++ b/dom/media/webspeech/synth/test/test_bfcache.html @@ -8,38 +8,39 @@ https://bugzilla.mozilla.org/show_bug.cg <title>Test for Bug 1230533: Test speech is stopped from a window when unloaded</title> <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <script type="application/javascript" src="common.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1230533">Mozilla Bug 1230533</a> <p id="display"></p> -<iframe id="testFrame"></iframe> <div id="content" style="display: none"> - + </div> <pre id="test"> <script type="application/javascript"> /** Test for Bug 525444 **/ SimpleTest.waitForExplicitFinish(); - -var iframe; +let testWin; function onDone() { + testWin.close(); SimpleTest.finish(); } SpecialPowers.pushPrefEnv({ set: [ ['media.webspeech.synth.enabled', true], - ['media.webspeech.synth.force_global_queue', true], - ['browser.sessionhistory.cache_subframes', true], - ['browser.sessionhistory.max_total_viewers', 10]] }, + ['media.webspeech.synth.force_global_queue', true]] }, function() { - loadSpeechTest("file_bfcache_frame.html"); + testWin = window.open("about:blank", "testWin"); + testWin.onload = function(e) { + waitForVoices(testWin) + .then(() => testWin.location = "file_bfcache_page1.html") + }; }); </script> </pre> </body> </html>
rename from dom/workers/test/WorkerDebugger_frozen_iframe1.html rename to dom/workers/test/WorkerDebugger_frozen_window1.html --- a/dom/workers/test/WorkerDebugger_frozen_iframe1.html +++ b/dom/workers/test/WorkerDebugger_frozen_window1.html @@ -1,15 +1,15 @@ <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <script> var worker = new Worker("WorkerDebugger_frozen_worker1.js"); worker.onmessage = function () { - parent.postMessage("ready", "*"); + opener.postMessage("ready", "*"); }; </script> </head> <body> This is page 1. </body> <html>
rename from dom/workers/test/WorkerDebugger_frozen_iframe2.html rename to dom/workers/test/WorkerDebugger_frozen_window2.html --- a/dom/workers/test/WorkerDebugger_frozen_iframe2.html +++ b/dom/workers/test/WorkerDebugger_frozen_window2.html @@ -1,15 +1,15 @@ <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <script> var worker = new Worker("WorkerDebugger_frozen_worker2.js"); worker.onmessage = function () { - parent.postMessage("ready", "*"); + opener.postMessage("ready", "*"); }; </script> </head> <body> This is page 2. </body> <html>
new file mode 100644 --- /dev/null +++ b/dom/workers/test/blank.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Blank</title> + </head> + <body onload="notifyOnload();"> + <script type="application/javascript"> + function notifyOnload() { + opener.postMessage({event: 'load'}, '*'); + } + </script> + </body> +</html> +
--- a/dom/workers/test/chrome.ini +++ b/dom/workers/test/chrome.ini @@ -19,18 +19,18 @@ support-files = WorkerDebuggerGlobalScope.reportError_childWorker.js WorkerDebuggerGlobalScope.reportError_debugger.js WorkerDebuggerGlobalScope.reportError_worker.js WorkerDebuggerGlobalScope.setImmediate_debugger.js WorkerDebuggerGlobalScope.setImmediate_worker.js WorkerDebuggerManager_childWorker.js WorkerDebuggerManager_worker.js WorkerDebugger_childWorker.js - WorkerDebugger_frozen_iframe1.html - WorkerDebugger_frozen_iframe2.html + WorkerDebugger_frozen_window1.html + WorkerDebugger_frozen_window2.html WorkerDebugger_frozen_worker1.js WorkerDebugger_frozen_worker2.js WorkerDebugger_promise_debugger.js WorkerDebugger_promise_worker.js WorkerDebugger_sharedWorker.js WorkerDebugger_suspended_debugger.js WorkerDebugger_suspended_worker.js WorkerDebugger_worker.js
--- a/dom/workers/test/mochitest.ini +++ b/dom/workers/test/mochitest.ini @@ -1,12 +1,13 @@ [DEFAULT] support-files = WorkerTest_badworker.js atob_worker.js + blank.html bug978260_worker.js bug1014466_data1.txt bug1014466_data2.txt bug1014466_worker.js bug1020226_worker.js bug1020226_frame.html bug998474_worker.js bug1063538_worker.js @@ -51,17 +52,17 @@ support-files = promise_worker.js recursion_worker.js recursiveOnerror_worker.js redirect_to_foreign.sjs rvals_worker.js sharedWorker_console.js sharedWorker_sharedWorker.js simpleThread_worker.js - suspend_iframe.html + suspend_window.html suspend_worker.js terminate_worker.js test_csp.html^headers^ test_csp.js referrer_worker.html threadErrors_worker1.js threadErrors_worker2.js threadErrors_worker3.js
--- a/dom/workers/test/multi_sharedWorker_frame.html +++ b/dom/workers/test/multi_sharedWorker_frame.html @@ -2,25 +2,41 @@ Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ --> <!DOCTYPE HTML> <html> <head> <title>Test for SharedWorker</title> </head> - <body> + <body onload="notifyOpenerLoadEvent();"> <script type="text/javascript"> "use strict"; + function postMessageToParentOrOpener(message) { + if (parent != window) { + parent.postMessage(message, "*"); + } + if (opener) { + opener.postMessage(message, "*"); + } + } + + // Used by test_multi_sharedWorker_lifetimes.html + function notifyOpenerLoadEvent() { + if (opener) { + opener.postMessage({event: "load"}, "*"); + } + } + function debug(message) { if (typeof(message) != "string") { throw new Error("debug() only accepts strings!"); } - parent.postMessage(message, "*"); + postMessageToParentOrOpener(message); } let worker; window.addEventListener("message", function(event) { if (!worker) { worker = new SharedWorker("multi_sharedWorker_sharedWorker.js", "FrameWorker"); @@ -30,22 +46,22 @@ let data = { type: "error", message: event.message, filename: event.filename, lineno: event.lineno, isErrorEvent: event instanceof ErrorEvent }; - parent.postMessage(data, "*"); + postMessageToParentOrOpener(data); }; worker.port.onmessage = function(event) { debug("Worker message: " + JSON.stringify(event.data)); - parent.postMessage(event.data, "*"); + postMessageToParentOrOpener(event.data); }; } debug("Posting message: " + JSON.stringify(event.data)); worker.port.postMessage(event.data); }); </script> </body>
--- a/dom/workers/test/test_WorkerDebugger_frozen.xul +++ b/dom/workers/test/test_WorkerDebugger_frozen.xul @@ -11,80 +11,69 @@ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/> <script type="application/javascript" src="dom_worker_helper.js"/> <script type="application/javascript"> <![CDATA[ - const CACHE_SUBFRAMES = "browser.sessionhistory.cache_subframes"; - const MAX_TOTAL_VIEWERS = "browser.sessionhistory.max_total_viewers"; - - const IFRAME1_URL = "WorkerDebugger_frozen_iframe1.html"; - const IFRAME2_URL = "WorkerDebugger_frozen_iframe2.html"; + const WINDOW1_URL = "WorkerDebugger_frozen_window1.html"; + const WINDOW2_URL = "WorkerDebugger_frozen_window2.html"; const WORKER1_URL = "WorkerDebugger_frozen_worker1.js"; const WORKER2_URL = "WorkerDebugger_frozen_worker2.js"; function test() { (async function() { SimpleTest.waitForExplicitFinish(); - var oldMaxTotalViewers = SpecialPowers.getIntPref(MAX_TOTAL_VIEWERS); - - SpecialPowers.setBoolPref(CACHE_SUBFRAMES, true); - SpecialPowers.setIntPref(MAX_TOTAL_VIEWERS, 10); - - let iframe = $("iframe"); + SpecialPowers.pushPrefEnv({set: + [["browser.sessionhistory.max_total_viewers", 10]]}); let promise = waitForMultiple([ waitForRegister(WORKER1_URL), waitForWindowMessage(window, "ready"), ]); - iframe.src = IFRAME1_URL; + let testWin = window.open(WINDOW1_URL, "testWin");; let [dbg1] = await promise; is(dbg1.isClosed, false, "debugger for worker on page 1 should not be closed"); promise = waitForMultiple([ waitForUnregister(WORKER1_URL), waitForDebuggerClose(dbg1), waitForRegister(WORKER2_URL), waitForWindowMessage(window, "ready"), ]); - iframe.src = IFRAME2_URL; + testWin.location = WINDOW2_URL; let [,, dbg2] = await promise; is(dbg1.isClosed, true, "debugger for worker on page 1 should be closed"); is(dbg2.isClosed, false, "debugger for worker on page 2 should not be closed"); promise = Promise.all([ waitForUnregister(WORKER2_URL), waitForDebuggerClose(dbg2), waitForRegister(WORKER1_URL) ]); - iframe.contentWindow.history.back(); + testWin.history.back(); [,, dbg1] = await promise; is(dbg1.isClosed, false, "debugger for worker on page 1 should not be closed"); is(dbg2.isClosed, true, "debugger for worker on page 2 should be closed"); - SpecialPowers.clearUserPref(CACHE_SUBFRAMES); - SpecialPowers.setIntPref(MAX_TOTAL_VIEWERS, oldMaxTotalViewers); - SimpleTest.finish(); })(); } ]]> </script> <body xmlns="http://www.w3.org/1999/xhtml"> <p id="display"></p> <div id="content" style="display:none;"></div> <pre id="test"></pre> - <iframe id="iframe"></iframe> </body> <label id="test-result"/> </window>
--- a/dom/workers/test/test_multi_sharedWorker_lifetimes.html +++ b/dom/workers/test/test_multi_sharedWorker_lifetimes.html @@ -7,149 +7,134 @@ <head> <title>Test for SharedWorker</title> <script src="/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> <script class="testbody" type="text/javascript"> "use strict"; const scrollbarPref = "layout.testing.overlay-scrollbars.always-visible"; - const bfCacheEnabledPref = "browser.sessionhistory.cache_subframes"; - const bfCacheDepthPref = "browser.sessionhistory.max_total_viewers"; - const bfCacheDepth = 10; + const viewersPref = "browser.sessionhistory.max_total_viewers"; - const frameRelativeURL = "multi_sharedWorker_frame.html"; + const windowRelativeURL = "multi_sharedWorker_frame.html"; const storedData = "0123456789abcdefghijklmnopqrstuvwxyz"; let testGenerator = (function*() { SimpleTest.waitForExplicitFinish(); - // Force scrollbar to always be shown. The scrollbar setting is - // necessary to avoid the fade-in/fade-out from evicting our document - // from the BF cache below. If bug 1049277 is fixed, then we can - // stop setting the scrollbar pref here. - SpecialPowers.pushPrefEnv({ set: [[scrollbarPref, true]] }, + // Force scrollbar to always be shown. The scrollbar setting is + // necessary to avoid the fade-in/fade-out from evicting our document + // from the BF cache below. If bug 1049277 is fixed, then we can + // stop setting the scrollbar pref here. + SpecialPowers.pushPrefEnv({ set: [[scrollbarPref, true], + [viewersPref, 10]] }, sendToGenerator); yield undefined; window.addEventListener("message", function(event) { if (typeof(event.data) == "string") { info(event.data); } else { sendToGenerator(event); } }); - let frame = document.getElementById("frame"); - frame.src = frameRelativeURL; - frame.onload = sendToGenerator; - + let testWin = window.open(windowRelativeURL, "testWin"); yield undefined; - frame = frame.contentWindow; - frame.postMessage({ command: "retrieve" }, "*"); + testWin.postMessage({ command: "retrieve" }, "*"); let event = yield undefined; ok(event instanceof MessageEvent, "Got a MessageEvent"); - is(event.source, frame, "Correct window got the event"); + is(event.source, testWin, "Correct window got the event"); is(event.data.type, "result", "Got a result message"); is(event.data.data, undefined, "No data stored yet"); - frame.postMessage({ command: "store", data: storedData }, "*"); - frame.postMessage({ command: "retrieve" }, "*"); + testWin.postMessage({ command: "store", data: storedData }, "*"); + testWin.postMessage({ command: "retrieve" }, "*"); event = yield undefined; ok(event instanceof MessageEvent, "Got a MessageEvent"); - is(event.source, frame, "Correct window got the event"); + is(event.source, testWin, "Correct window got the event"); is(event.data.type, "result", "Got a result message"); is(event.data.data, storedData, "Got stored data"); // Navigate when the bfcache is disabled. - info("Navigating to about:blank"); - frame = document.getElementById("frame"); - frame.onload = sendToGenerator; - frame.src = "about:blank"; - frame.contentWindow.document.body.offsetTop; + info("Navigating to a blank page"); + testWin.onunload = function(){};; + testWin.location = "blank.html"; + testWin.document.body.offsetTop; yield undefined; - info("Navigating to " + frameRelativeURL); - frame.src = frameRelativeURL; - frame.contentWindow.document.body.offsetTop; + info("Navigating to " + windowRelativeURL); + testWin.onunload = function(){}; + testWin.location = windowRelativeURL; + testWin.document.body.offsetTop; yield undefined; - frame = frame.contentWindow; - frame.postMessage({ command: "retrieve" }, "*"); + testWin.postMessage({ command: "retrieve" }, "*"); event = yield undefined; ok(event instanceof MessageEvent, "Got a MessageEvent"); - is(event.source, frame, "Correct window got the event"); + is(event.source, testWin, "Correct window got the event"); is(event.data.type, "result", "Got a result message"); is(event.data.data, undefined, "No data stored"); - frame.postMessage({ command: "store", data: storedData }, "*"); - frame.postMessage({ command: "retrieve" }, "*"); + testWin.postMessage({ command: "store", data: storedData }, "*"); + testWin.postMessage({ command: "retrieve" }, "*"); event = yield undefined; ok(event instanceof MessageEvent, "Got a MessageEvent"); - is(event.source, frame, "Correct window got the event"); + is(event.source, testWin, "Correct window got the event"); is(event.data.type, "result", "Got a result message"); is(event.data.data, storedData, "Got stored data"); - info("Enabling '" + bfCacheEnabledPref + "' pref"); - SpecialPowers.pushPrefEnv({ set: [[bfCacheEnabledPref, true], - [bfCacheDepthPref, bfCacheDepth]] }, - sendToGenerator); - yield undefined; - // Navigate when the bfcache is enabled. - frame = document.getElementById("frame"); - frame.onload = sendToGenerator; - - info("Navigating to about:blank"); - frame.src = "about:blank"; - frame.contentWindow.document.body.offsetTop; + info("Navigating to a blank page"); + testWin.location = "blank.html"; + testWin.document.body.offsetTop; yield undefined; for (let i = 0; i < 3; i++) { info("Running GC"); SpecialPowers.exactGC(sendToGenerator); yield undefined; + // It seems using SpecialPowers.executeSoon() would make the + // entryGlobal being the TabChildGlobal (and that would make the + // baseURI in the location assignment below being incorrect); + // setTimeout on the otherhand ensures the entryGlobal is this + // window. info("Waiting the event queue to clear"); - SpecialPowers.executeSoon(sendToGenerator); + setTimeout(sendToGenerator, 0); yield undefined; } - info("Navigating to " + frameRelativeURL); - frame.src = frameRelativeURL; - frame.contentWindow.document.body.offsetTop; + info("Navigating to " + windowRelativeURL); + testWin.location = windowRelativeURL; + testWin.document.body.offsetTop; yield undefined; - frame = frame.contentWindow; - frame.postMessage({ command: "retrieve" }, "*"); + testWin.postMessage({ command: "retrieve" }, "*"); event = yield undefined; ok(event instanceof MessageEvent, "Got a MessageEvent"); - is(event.source, frame, "Correct window got the event"); + is(event.source, testWin, "Correct window got the event"); is(event.data.type, "result", "Got a result message"); is(event.data.data, storedData, "Still have data stored"); - info("Resetting '" + bfCacheEnabledPref + "' pref"); - SpecialPowers.popPrefEnv(sendToGenerator); - yield undefined; - window.removeEventListener("message", sendToGenerator); + testWin.close(); SimpleTest.finish(); })(); let sendToGenerator = testGenerator.next.bind(testGenerator); </script> </head> <body onload="testGenerator.next();"> - <iframe id="frame"></iframe> </body> </html>
--- a/dom/workers/test/test_onLine.html +++ b/dom/workers/test/test_onLine.html @@ -21,17 +21,26 @@ http://creativecommons.org/licenses/publ <script class="testbody" type="text/javascript"> addLoadEvent(function() { var w = new Worker("onLine_worker.js"); w.onmessage = function(e) { if (e.data.type === 'ready') { - doTest(); + // XXX Important trick here. + // + // Setting iosvc.offline would trigger a sync notifyObservers call, and if + // there exists a preloaded about:newtab (see tabbrowser._handleNewTab), + // that tab will be notified. + // + // This implies a sync call across different tabGroups, and will hit the + // assertion in SchedulerGroup::ValidateAccess(). So use executeSoon to + // re-dispatch an unlabeled runnable to the event queue. + SpecialPowers.executeSoon(doTest); } else if (e.data.type === 'ok') { ok(e.data.test, e.data.message); } else if (e.data.type === 'finished') { SimpleTest.finish(); } } function doTest() {
--- a/dom/workers/test/test_suspend.html +++ b/dom/workers/test/test_suspend.html @@ -9,130 +9,126 @@ <title>Test for DOM Worker Threads</title> <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> </head> <body> <p id="display"></p> <div id="content" style="display: none"></div> <pre id="test"> -<iframe id="workerFrame" src="suspend_iframe.html" onload="subframeLoaded();"> -</iframe> <script class="testbody" type="text/javascript"> SimpleTest.waitForExplicitFinish(); - var iframe; + const BLANK_URI = location.href.replace("test_suspend.html", "blank.html"); + var lastCount; var suspended = false; var resumed = false; var finished = false; var interval; var oldMessageCount; var waitCount = 0; + var testWin = window.open("suspend_window.html", "testWin"); + testWin.onload = testWinLoaded; + + window.addEventListener("message", msg => { + if (suspended) { + badOnloadCallback(); + } else { + suspendCallback(); + } + }) + function finishTest() { if (finished) { return; } finished = true; - SpecialPowers.flushPrefEnv(function () { - iframe.terminateWorker(); - SimpleTest.finish(); - }); + testWin.terminateWorker(); + testWin.close(); + SimpleTest.finish(); } function waitInterval() { if (finished) { return; } - is(String(iframe.location), "about:blank", "Wrong url!"); + is(testWin.location.href, BLANK_URI, "Wrong url!"); is(suspended, true, "Not suspended?"); is(resumed, false, "Already resumed?!"); is(lastCount, oldMessageCount, "Received a message while suspended!"); if (++waitCount == 5) { clearInterval(interval); resumed = true; - iframe.history.back(); + testWin.history.back(); } } function badOnloadCallback() { if (finished) { return; } - ok(false, "We don't want suspend_iframe.html to fire a new load event, we want it to come out of the bfcache!"); + ok(false, "We don't want suspend_window.html to fire a new load event, we want it to come out of the bfcache!"); finishTest(); } function suspendCallback() { if (finished) { return; } - is(String(iframe.location), "about:blank", "Wrong url!"); + is(testWin.location.href, BLANK_URI, "Wrong url!"); is(suspended, false, "Already suspended?"); is(resumed, false, "Already resumed?"); - SpecialPowers.popPrefEnv(function () { - suspended = true; - var iframeElement = document.getElementById("workerFrame"); - iframeElement.onload = badOnloadCallback; - oldMessageCount = lastCount; - interval = setInterval(waitInterval, 1000); - }); + suspended = true; + oldMessageCount = lastCount; + interval = setInterval(waitInterval, 1000); } function messageCallback(data) { if (finished) { return; } if (!suspended) { ok(lastCount === undefined || lastCount == data - 1, "Got good data, lastCount = " + lastCount + ", data = " + data); lastCount = data; if (lastCount == 25) { - SpecialPowers.pushPrefEnv({"set": [["browser.sessionhistory.cache_subframes", true]]}, function () { - iframe.location = "about:blank"; - // We want suspend_iframe.html to go into bfcache, so we need to flush - // out all pending notifications. Otherwise, if they're flushed too - // late, they could kick us out of the bfcache again. - iframe.document.body.offsetTop; - }); + testWin.location = "blank.html"; + // We want suspend_window.html to go into bfcache, so we need to flush + // out all pending notifications. Otherwise, if they're flushed too + // late, they could kick us out of the bfcache again. + testWin.document.body.offsetTop; } return; } - var newLocation = - window.location.toString().replace("test_suspend.html", - "suspend_iframe.html"); - is(newLocation.indexOf(iframe.location.toString()), 0, "Wrong url!"); + var newLocation = location.href.replace("test_suspend.html", + "suspend_window.html"); + is(testWin.location.href, newLocation, "Wrong url!"); is(resumed, true, "Got message before resumed!"); is(lastCount, data - 1, "Missed a message, suspend failed!"); finishTest(); } function errorCallback(data) { if (finished) { return; } - ok(false, "Iframe had an error: '" + data + "'"); + ok(false, "testWin had an error: '" + data + "'"); finishTest(); } - function subframeLoaded() { + function testWinLoaded() { if (finished) { return; } - var iframeElement = document.getElementById("workerFrame"); - iframeElement.onload = suspendCallback; - - iframe = iframeElement.contentWindow; - ok(iframe, "No iframe?!"); - - iframe.startWorker(messageCallback, errorCallback); + testWin.startWorker(messageCallback, errorCallback); } </script> </pre> </body> </html>
--- a/gfx/layers/wr/WebRenderLayerManager.cpp +++ b/gfx/layers/wr/WebRenderLayerManager.cpp @@ -459,46 +459,70 @@ WebRenderLayerManager::PushImage(nsDispl return true; } static void PaintItemByDrawTarget(nsDisplayItem* aItem, DrawTarget* aDT, const LayerRect& aImageRect, const LayerPoint& aOffset, - nsDisplayListBuilder* aDisplayListBuilder) + nsDisplayListBuilder* aDisplayListBuilder, + RefPtr<BasicLayerManager>& aManager, + WebRenderLayerManager* aWrManager) { MOZ_ASSERT(aDT); aDT->ClearRect(aImageRect.ToUnknownRect()); RefPtr<gfxContext> context = gfxContext::CreateOrNull(aDT); MOZ_ASSERT(context); context->SetMatrix(gfxMatrix::Translation(-aOffset.x, -aOffset.y)); switch (aItem->GetType()) { case DisplayItemType::TYPE_MASK: static_cast<nsDisplayMask*>(aItem)->PaintMask(aDisplayListBuilder, context); break; case DisplayItemType::TYPE_FILTER: { - RefPtr<BasicLayerManager> tempManager = new BasicLayerManager(BasicLayerManager::BLM_INACTIVE); - FrameLayerBuilder* layerBuilder = new FrameLayerBuilder(); - layerBuilder->Init(aDisplayListBuilder, tempManager); - - tempManager->BeginTransactionWithTarget(context); - ContainerLayerParameters param; - RefPtr<Layer> layer = aItem->BuildLayer(aDisplayListBuilder, tempManager, param); - if (layer) { - tempManager->SetRoot(layer); - static_cast<nsDisplayFilter*>(aItem)->PaintAsLayer(aDisplayListBuilder, context, tempManager); + if (aManager == nullptr) { + aManager = new BasicLayerManager(BasicLayerManager::BLM_INACTIVE); } - if (tempManager->InTransaction()) { - tempManager->AbortTransaction(); + FrameLayerBuilder* layerBuilder = new FrameLayerBuilder(); + layerBuilder->Init(aDisplayListBuilder, aManager); + layerBuilder->DidBeginRetainedLayerTransaction(aManager); + + aManager->BeginTransactionWithTarget(context); + + ContainerLayerParameters param; + RefPtr<Layer> layer = + static_cast<nsDisplayFilter*>(aItem)->BuildLayer(aDisplayListBuilder, + aManager, param); + + if (layer) { + UniquePtr<LayerProperties> props; + props = Move(LayerProperties::CloneFrom(aManager->GetRoot())); + + aManager->SetRoot(layer); + layerBuilder->WillEndTransaction(); + + nsIntRegion invalid; + props->ComputeDifferences(layer, invalid, nullptr); + + static_cast<nsDisplayFilter*>(aItem)->PaintAsLayer(aDisplayListBuilder, + context, aManager); + + if (!invalid.IsEmpty()) { + aWrManager->SetNotifyInvalidation(true); + } } + + if (aManager->InTransaction()) { + aManager->AbortTransaction(); + } + aManager->SetTarget(nullptr); break; } default: aItem->Paint(aDisplayListBuilder, context); break; } if (gfxPrefs::WebRenderHighlightPaintedLayers()) { @@ -549,51 +573,60 @@ WebRenderLayerManager::GenerateFallbackD LayerIntSize imageSize = RoundedToInt(bounds.Size()); aImageRect = LayerRect(LayerPoint(0, 0), LayerSize(imageSize)); if (imageSize.width == 0 || imageSize.height == 0) { return nullptr; } aOffset = RoundedToInt(bounds.TopLeft()); - nsRegion invalidRegion; + + bool needPaint = true; nsAutoPtr<nsDisplayItemGeometry> geometry = fallbackData->GetGeometry(); - if (geometry) { + + // nsDisplayFilter is rendered via BasicLayerManager which means the invalidate + // region is unknown until we traverse the displaylist contained by it. + if (geometry && !fallbackData->IsInvalid() && + aItem->GetType() != DisplayItemType::TYPE_FILTER) { nsRect invalid; + nsRegion invalidRegion; + if (aItem->IsInvalid(invalid)) { invalidRegion.OrWith(clippedBounds); } else { nsPoint shift = itemBounds.TopLeft() - geometry->mBounds.TopLeft(); geometry->MoveBy(shift); aItem->ComputeInvalidationRegion(aDisplayListBuilder, geometry, &invalidRegion); nsRect lastBounds = fallbackData->GetBounds(); lastBounds.MoveBy(shift); if (!lastBounds.IsEqualInterior(clippedBounds)) { invalidRegion.OrWith(lastBounds); invalidRegion.OrWith(clippedBounds); } } + needPaint = !invalidRegion.IsEmpty(); } - gfx::SurfaceFormat format = aItem->GetType() == DisplayItemType::TYPE_MASK ? - gfx::SurfaceFormat::A8 : gfx::SurfaceFormat::B8G8R8A8; - if (!geometry || !invalidRegion.IsEmpty() || fallbackData->IsInvalid()) { + if (needPaint) { + gfx::SurfaceFormat format = aItem->GetType() == DisplayItemType::TYPE_MASK ? + gfx::SurfaceFormat::A8 : gfx::SurfaceFormat::B8G8R8A8; if (gfxPrefs::WebRenderBlobImages()) { bool snapped; bool isOpaque = aItem->GetOpaqueRegion(aDisplayListBuilder, &snapped).Contains(clippedBounds); RefPtr<gfx::DrawEventRecorderMemory> recorder = MakeAndAddRef<gfx::DrawEventRecorderMemory>(); // TODO: should use 'format' to replace gfx::SurfaceFormat::B8G8R8A8. Currently blob image doesn't support A8 format. RefPtr<gfx::DrawTarget> dummyDt = gfx::Factory::CreateDrawTarget(gfx::BackendType::SKIA, gfx::IntSize(1, 1), gfx::SurfaceFormat::B8G8R8A8); RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateRecordingDrawTarget(recorder, dummyDt, imageSize.ToUnknownSize()); - PaintItemByDrawTarget(aItem, dt, aImageRect, aOffset, aDisplayListBuilder); + PaintItemByDrawTarget(aItem, dt, aImageRect, aOffset, aDisplayListBuilder, + fallbackData->mBasicLayerManager, this); recorder->Finish(); Range<uint8_t> bytes((uint8_t*)recorder->mOutputStream.mData, recorder->mOutputStream.mLength); wr::ImageKey key = WrBridge()->GetNextImageKey(); wr::ImageDescriptor descriptor(imageSize.ToUnknownSize(), 0, dt->GetFormat(), isOpaque); aResources.AddBlobImage(key, descriptor, bytes); fallbackData->SetKey(key); } else { @@ -603,17 +636,19 @@ WebRenderLayerManager::GenerateFallbackD { UpdateImageHelper helper(imageContainer, imageClient, imageSize.ToUnknownSize(), format); { RefPtr<gfx::DrawTarget> dt = helper.GetDrawTarget(); if (!dt) { return nullptr; } - PaintItemByDrawTarget(aItem, dt, aImageRect, aOffset, aDisplayListBuilder); + PaintItemByDrawTarget(aItem, dt, aImageRect, aOffset, + aDisplayListBuilder, + fallbackData->mBasicLayerManager, this); } if (!helper.UpdateImage()) { return nullptr; } } // Force update the key in fallback data since we repaint the image in this path. // If not force update, fallbackData may reuse the original key because it
--- a/gfx/layers/wr/WebRenderLayerManager.h +++ b/gfx/layers/wr/WebRenderLayerManager.h @@ -241,16 +241,17 @@ public: if (T::Type() == WebRenderUserData::UserDataType::eCanvas) { mLastCanvasDatas.PutEntry(data->AsCanvasData()); } RefPtr<T> res = static_cast<T*>(data.get()); return res.forget(); } bool ShouldNotifyInvalidation() const { return mShouldNotifyInvalidation; } + void SetNotifyInvalidation(bool aShouldNotifyInvalidation) { mShouldNotifyInvalidation = aShouldNotifyInvalidation; } private: /** * Take a snapshot of the parent context, and copy * it into mTarget. */ void MakeSnapshotIfRequired(LayoutDeviceIntSize aSize);
--- a/gfx/layers/wr/WebRenderUserData.h +++ b/gfx/layers/wr/WebRenderUserData.h @@ -1,16 +1,17 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef GFX_WEBRENDERUSERDATA_H #define GFX_WEBRENDERUSERDATA_H +#include "BasicLayers.h" // for BasicLayerManager #include "mozilla/layers/StackingContextHelper.h" #include "mozilla/webrender/WebRenderAPI.h" #include "mozilla/layers/AnimationInfo.h" class nsDisplayItemGeometry; namespace mozilla { namespace wr { @@ -122,16 +123,17 @@ public: static UserDataType Type() { return UserDataType::eFallback; } nsAutoPtr<nsDisplayItemGeometry> GetGeometry(); void SetGeometry(nsAutoPtr<nsDisplayItemGeometry> aGeometry); nsRect GetBounds() { return mBounds; } void SetBounds(const nsRect& aRect) { mBounds = aRect; } void SetInvalid(bool aInvalid) { mInvalid = aInvalid; } bool IsInvalid() { return mInvalid; } + RefPtr<BasicLayerManager> mBasicLayerManager; protected: nsAutoPtr<nsDisplayItemGeometry> mGeometry; nsRect mBounds; bool mInvalid; }; class WebRenderAnimationData : public WebRenderUserData {
--- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -1502,16 +1502,18 @@ nsDocumentViewer::Open(nsISupports *aSta nsCOMPtr<nsIDocShell> shell = do_QueryInterface(item); AttachContainerRecurse(shell); } } SyncParentSubDocMap(); if (mFocusListener && mDocument) { + // The focus listener may have been disconnected. + mFocusListener->Init(this); mDocument->AddEventListener(NS_LITERAL_STRING("focus"), mFocusListener, false, false); mDocument->AddEventListener(NS_LITERAL_STRING("blur"), mFocusListener, false, false); } // XXX re-enable image animations once that works correctly
--- a/layout/painting/nsDisplayList.cpp +++ b/layout/painting/nsDisplayList.cpp @@ -9251,17 +9251,16 @@ nsDisplayFilter::BuildLayer(nsDisplayLis "By getting here, we must have valid CSS filters."); ContainerLayerParameters newContainerParameters = aContainerParameters; newContainerParameters.mDisableSubpixelAntialiasingInDescendants = true; RefPtr<ContainerLayer> container = aManager->GetLayerBuilder()-> BuildContainerLayerFor(aBuilder, aManager, mFrame, this, &mList, newContainerParameters, nullptr); - LayerState state = this->GetLayerState(aBuilder, aManager, newContainerParameters); if (container && state != LAYER_SVG_EFFECTS) { const nsTArray<nsStyleFilter>& filters = mFrame->StyleEffects()->mFilters; nsTArray<layers::CSSFilter> cssFilters = nsTArray<layers::CSSFilter>(filters.Length()); for (const nsStyleFilter& filter : filters) { cssFilters.AppendElement(ToCSSFilter(filter)); }
--- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1744,17 +1744,17 @@ fuzzy-if(true,17,5886) fuzzy-if(skiaCont == 776265-2a.html 776265-2-ref.html == 776265-2b.html 776265-2-ref.html == 776265-2c.html 776265-2-ref.html == 776265-2d.html 776265-2-ref.html == 776443-1.html 776443-1-ref.html == 776443-2.html 776443-2-ref.html == 786254-1.html 786254-1-ref.html == 787947-1.html 787947-1-ref.html -== 796847-1.svg 796847-1-ref.svg +pref(gfx.webrender.layers-free,true) == 796847-1.svg 796847-1-ref.svg fuzzy(40,875) fuzzy-if(skiaContent,1,2500) == 797797-1.html 797797-1-ref.html # 'opacity:N' and rgba(,,,N) text don't match precisely fuzzy(40,850) fuzzy-if(skiaContent,2,2310) == 797797-2.html 797797-2-ref.html # 'opacity:N' and rgba(,,,N) text don't match precisely == 801994-1.html 801994-1-ref.html == 804323-1.html 804323-1-ref.html fuzzy-if(Android,8,608) == 811301-1.html 811301-1-ref.html == 812824-1.html 812824-1-ref.html == 814677.html 814677-ref.html == 814952-1.html 814952-1-ref.html