diff --git a/accessible/windows/msaa/AccessibleWrap.cpp b/accessible/windows/msaa/AccessibleWrap.cpp index c4e613fe2af..30d8994cfe5 100644 --- a/accessible/windows/msaa/AccessibleWrap.cpp +++ b/accessible/windows/msaa/AccessibleWrap.cpp @@ -1199,6 +1199,41 @@ AccessibleWrap::GetNativeInterface(void** aOutAccessible) NS_ADDREF_THIS(); } +void +AccessibleWrap::FireWinEvent(Accessible* aTarget, uint32_t aEventType) +{ + static_assert(sizeof(gWinEventMap)/sizeof(gWinEventMap[0]) == nsIAccessibleEvent::EVENT_LAST_ENTRY, + "MSAA event map skewed"); + + NS_ASSERTION(aEventType > 0 && aEventType < ArrayLength(gWinEventMap), "invalid event type"); + + uint32_t winEvent = gWinEventMap[aEventType]; + if (!winEvent) + return; + + int32_t childID = GetChildIDFor(aTarget); + if (!childID) + return; // Can't fire an event without a child ID + + HWND hwnd = GetHWNDFor(aTarget); + if (!hwnd) { + return; + } + + // Fire MSAA event for client area window. + ::NotifyWinEvent(winEvent, hwnd, OBJID_CLIENT, childID); + + // JAWS announces collapsed combobox navigation based on focus events. + if (aEventType == nsIAccessibleEvent::EVENT_SELECTION && + Compatibility::IsJAWS()) { + roles::Role role = aTarget->IsProxy() ? aTarget->Proxy()->Role() : + aTarget->Role(); + if (role == roles::COMBOBOX_OPTION) { + ::NotifyWinEvent(EVENT_OBJECT_FOCUS, hwnd, OBJID_CLIENT, childID); + } + } +} + //////////////////////////////////////////////////////////////////////////////// // Accessible @@ -1214,15 +1249,6 @@ AccessibleWrap::HandleAccEvent(AccEvent* aEvent) uint32_t eventType = aEvent->GetEventType(); - static_assert(sizeof(gWinEventMap)/sizeof(gWinEventMap[0]) == nsIAccessibleEvent::EVENT_LAST_ENTRY, - "MSAA event map skewed"); - - NS_ENSURE_TRUE(eventType > 0 && eventType < ArrayLength(gWinEventMap), NS_ERROR_FAILURE); - - uint32_t winEvent = gWinEventMap[eventType]; - if (!winEvent) - return NS_OK; - // Means we're not active. NS_ENSURE_TRUE(!IsDefunct(), NS_ERROR_FAILURE); @@ -1235,41 +1261,7 @@ AccessibleWrap::HandleAccEvent(AccEvent* aEvent) UpdateSystemCaretFor(accessible); } - int32_t childID = GetChildIDFor(accessible); // get the id for the accessible - if (!childID) - return NS_OK; // Can't fire an event without a child ID - - HWND hWnd = GetHWNDFor(accessible); - NS_ENSURE_TRUE(hWnd, NS_ERROR_FAILURE); - - nsAutoString tag; - nsAutoCString id; - nsIContent* cnt = accessible->GetContent(); - if (cnt) { - cnt->NodeInfo()->NameAtom()->ToString(tag); - nsIAtom* aid = cnt->GetID(); - if (aid) - aid->ToUTF8String(id); - } - -#ifdef A11Y_LOG - if (logging::IsEnabled(logging::ePlatforms)) { - printf("\n\nMSAA event: event: %d, target: %s@id='%s', childid: %d, hwnd: %p\n\n", - eventType, NS_ConvertUTF16toUTF8(tag).get(), id.get(), - childID, hWnd); - } -#endif - - // Fire MSAA event for client area window. - ::NotifyWinEvent(winEvent, hWnd, OBJID_CLIENT, childID); - - // JAWS announces collapsed combobox navigation based on focus events. - if (Compatibility::IsJAWS()) { - if (eventType == nsIAccessibleEvent::EVENT_SELECTION && - accessible->Role() == roles::COMBOBOX_OPTION) { - ::NotifyWinEvent(EVENT_OBJECT_FOCUS, hWnd, OBJID_CLIENT, childID); - } - } + FireWinEvent(accessible, eventType); return NS_OK; } diff --git a/accessible/windows/msaa/AccessibleWrap.h b/accessible/windows/msaa/AccessibleWrap.h index 771dad5a02c..fc0b8bedab3 100644 --- a/accessible/windows/msaa/AccessibleWrap.h +++ b/accessible/windows/msaa/AccessibleWrap.h @@ -159,6 +159,8 @@ public: // construction, destruction static int32_t GetChildIDFor(Accessible* aAccessible); static HWND GetHWNDFor(Accessible* aAccessible); + static void FireWinEvent(Accessible* aTarget, uint32_t aEventType); + /** * System caret support: update the Windows caret position. * The system caret works more universally than the MSAA caret diff --git a/accessible/windows/msaa/Platform.cpp b/accessible/windows/msaa/Platform.cpp index 791205ccd85..74cc6d131ac 100644 --- a/accessible/windows/msaa/Platform.cpp +++ b/accessible/windows/msaa/Platform.cpp @@ -76,18 +76,23 @@ a11y::ProxyDestroyed(ProxyAccessible* aProxy) } void -a11y::ProxyEvent(ProxyAccessible*, uint32_t) +a11y::ProxyEvent(ProxyAccessible* aTarget, uint32_t aEventType) { + AccessibleWrap::FireWinEvent(WrapperFor(aTarget), aEventType); } void -a11y::ProxyStateChangeEvent(ProxyAccessible*, uint64_t, bool) +a11y::ProxyStateChangeEvent(ProxyAccessible* aTarget, uint64_t, bool) { + AccessibleWrap::FireWinEvent(WrapperFor(aTarget), + nsIAccessibleEvent::EVENT_STATE_CHANGE); } void a11y::ProxyCaretMoveEvent(ProxyAccessible* aTarget, int32_t aOffset) { + AccessibleWrap::FireWinEvent(WrapperFor(aTarget), + nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED); } void @@ -104,4 +109,8 @@ a11y::ProxyTextChangeEvent(ProxyAccessible* aText, const nsString& aStr, if (text) { ia2AccessibleText::UpdateTextChangeData(text, aInsert, aStr, aStart, aLen); } + + uint32_t eventType = aInsert ? nsIAccessibleEvent::EVENT_TEXT_INSERTED : + nsIAccessibleEvent::EVENT_TEXT_REMOVED; + AccessibleWrap::FireWinEvent(wrapper, eventType); } diff --git a/browser/base/content/test/general/browser_domFullscreen_fullscreenMode.js b/browser/base/content/test/general/browser_domFullscreen_fullscreenMode.js index e6d5ee41470..83356b195b8 100644 --- a/browser/base/content/test/general/browser_domFullscreen_fullscreenMode.js +++ b/browser/base/content/test/general/browser_domFullscreen_fullscreenMode.js @@ -120,12 +120,31 @@ var gTests = [ } ]; +function checkState(expectedStates, contentStates) { + is(contentStates.inDOMFullscreen, expectedStates.inDOMFullscreen, + "The DOM fullscreen state of the content should match"); + // TODO window.fullScreen is not updated as soon as the fullscreen + // state flips in child process, hence checking it could cause + // anonying intermittent failure. As we just want to confirm the + // fullscreen state of the browser window, we can just check the + // that on the chrome window below. + // is(contentStates.inFullscreen, expectedStates.inFullscreen, + // "The fullscreen state of the content should match"); + is(document.mozFullScreen, expectedStates.inDOMFullscreen, + "The DOM fullscreen state of the chrome should match"); + is(window.fullScreen, expectedStates.inFullscreen, + "The fullscreen state of the chrome should match"); +} + +const kPage = "http://example.org/browser/browser/" + + "base/content/test/general/dummy_page.html"; + add_task(function* () { yield pushPrefs( ["full-screen-api.transition-duration.enter", "0 0"], ["full-screen-api.transition-duration.leave", "0 0"]); - let tab = gBrowser.addTab("about:robots"); + let tab = gBrowser.addTab(kPage); let browser = tab.linkedBrowser; gBrowser.selectedTab = tab; yield waitForDocLoadComplete(); @@ -148,63 +167,53 @@ add_task(function* () { yield new Promise(resolve => listenOneMessage("Test:Activated", resolve)); for (let test of gTests) { + let contentStates; info("Testing exit DOM fullscreen via " + test.desc); - var { inDOMFullscreen, inFullscreen } = yield queryFullscreenState(); - ok(!inDOMFullscreen, "Shouldn't have been in DOM fullscreen"); - ok(!inFullscreen, "Shouldn't have been in fullscreen"); + contentStates = yield queryFullscreenState(); + checkState({inDOMFullscreen: false, inFullscreen: false}, contentStates); /* DOM fullscreen without fullscreen mode */ - // Enter DOM fullscreen + info("> Enter DOM fullscreen"); gMessageManager.sendAsyncMessage("Test:RequestFullscreen"); - var { inDOMFullscreen, inFullscreen } = - yield waitForFullscreenChanges(FS_CHANGE_BOTH); - ok(inDOMFullscreen, "Should now be in DOM fullscreen"); - ok(inFullscreen, "Should now be in fullscreen"); + contentStates = yield waitForFullscreenChanges(FS_CHANGE_BOTH); + checkState({inDOMFullscreen: true, inFullscreen: true}, contentStates); - // Exit DOM fullscreen + info("> Exit DOM fullscreen"); test.exitFunc(); - var { inDOMFullscreen, inFullscreen } = - yield waitForFullscreenChanges(FS_CHANGE_BOTH); - ok(!inDOMFullscreen, "Should no longer be in DOM fullscreen"); - ok(!inFullscreen, "Should no longer be in fullscreen"); + contentStates = yield waitForFullscreenChanges(FS_CHANGE_BOTH); + checkState({inDOMFullscreen: false, inFullscreen: false}, contentStates); /* DOM fullscreen with fullscreen mode */ - // Enter fullscreen mode + info("> Enter fullscreen mode"); // Need to be asynchronous because sizemodechange event could be // dispatched synchronously, which would cause the event listener // miss that event and wait infinitely. executeSoon(() => BrowserFullScreen()); - var { inDOMFullscreen, inFullscreen } = - yield waitForFullscreenChanges(FS_CHANGE_SIZE); - ok(!inDOMFullscreen, "Shouldn't have been in DOM fullscreen"); - ok(inFullscreen, "Should now be in fullscreen mode"); + contentStates = yield waitForFullscreenChanges(FS_CHANGE_SIZE); + checkState({inDOMFullscreen: false, inFullscreen: true}, contentStates); - // Enter DOM fullscreen + info("> Enter DOM fullscreen in fullscreen mode"); gMessageManager.sendAsyncMessage("Test:RequestFullscreen"); - var { inDOMFullscreen, inFullscreen } = - yield waitForFullscreenChanges(FS_CHANGE_DOM); - ok(inDOMFullscreen, "Should now be in DOM fullscreen"); - ok(inFullscreen, "Should still be in fullscreen"); + contentStates = yield waitForFullscreenChanges(FS_CHANGE_DOM); + checkState({inDOMFullscreen: true, inFullscreen: true}, contentStates); - // Exit DOM fullscreen + info("> Exit DOM fullscreen in fullscreen mode"); test.exitFunc(); - var { inDOMFullscreen, inFullscreen } = - yield waitForFullscreenChanges(test.affectsFullscreenMode ? - FS_CHANGE_BOTH : FS_CHANGE_DOM); - ok(!inDOMFullscreen, "Should no longer be in DOM fullscreen"); - if (test.affectsFullscreenMode) { - ok(!inFullscreen, "Should no longer be in fullscreen mode"); - } else { - ok(inFullscreen, "Should still be in fullscreen mode"); - } + contentStates = yield waitForFullscreenChanges( + test.affectsFullscreenMode ? FS_CHANGE_BOTH : FS_CHANGE_DOM); + checkState({ + inDOMFullscreen: false, + inFullscreen: !test.affectsFullscreenMode + }, contentStates); /* Cleanup */ // Exit fullscreen mode if we are still in - if (browser.contentWindow.fullScreen) { + if (window.fullScreen) { + info("> Cleanup"); executeSoon(() => BrowserFullScreen()); yield waitForFullscreenChanges(FS_CHANGE_SIZE); } diff --git a/build/mach_bootstrap.py b/build/mach_bootstrap.py index 66752dfe3dc..5553991ca6e 100644 --- a/build/mach_bootstrap.py +++ b/build/mach_bootstrap.py @@ -145,6 +145,7 @@ MACH_MODULES = [ 'tools/docs/mach_commands.py', 'tools/mercurial/mach_commands.py', 'tools/mach_commands.py', + 'tools/power/mach_commands.py', 'mobile/android/mach_commands.py', ] diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index a5f7b0981eb..4373cc944ca 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -232,12 +232,11 @@ using namespace mozilla; using namespace mozilla::dom; using mozilla::dom::workers::ServiceWorkerManager; -// True means sUseErrorPages and sInterceptionEnabled has been added to +// True means sUseErrorPages has been added to // preferences var cache. static bool gAddedPreferencesVarCache = false; bool nsDocShell::sUseErrorPages = false; -bool nsDocShell::sInterceptionEnabled = false; // Number of documents currently loading static int32_t gNumberOfDocumentsLoading = 0; @@ -5536,9 +5535,6 @@ nsDocShell::Create() Preferences::AddBoolVarCache(&sUseErrorPages, "browser.xul.error_pages.enabled", mUseErrorPages); - Preferences::AddBoolVarCache(&sInterceptionEnabled, - "dom.serviceWorkers.interception.enabled", - false); gAddedPreferencesVarCache = true; } @@ -13858,7 +13854,7 @@ nsDocShell::ShouldPrepareForIntercept(nsIURI* aURI, bool aIsNavigate, { *aShouldIntercept = false; // Preffed off. - if (!sInterceptionEnabled) { + if (!nsContentUtils::ServiceWorkerInterceptionEnabled()) { return NS_OK; } diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h index 301ed55ddc7..943876232b3 100644 --- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -887,9 +887,6 @@ protected: // Cached value of the "browser.xul.error_pages.enabled" preference. static bool sUseErrorPages; - // Cached value of the "dom.serviceWorkers.interception.enabled" preference. - static bool sInterceptionEnabled; - bool mCreated; bool mAllowSubframes; bool mAllowPlugins; diff --git a/dom/base/nsContentSink.cpp b/dom/base/nsContentSink.cpp index 8abb4871693..4b61f63de8a 100644 --- a/dom/base/nsContentSink.cpp +++ b/dom/base/nsContentSink.cpp @@ -1064,6 +1064,12 @@ nsContentSink::ProcessOfflineManifest(const nsAString& aManifestSpec) return; } + // If this document has been interecepted, let's skip the processing of the + // manifest. + if (nsContentUtils::IsControlledByServiceWorker(mDocument)) { + return; + } + // If the docshell's in private browsing mode, we don't want to do any // manifest processing. nsCOMPtr loadContext = do_QueryInterface(mDocShell); diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index 47fd964dcdf..bb06e960f92 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -52,6 +52,7 @@ #include "mozilla/dom/TouchEvent.h" #include "mozilla/dom/ShadowRoot.h" #include "mozilla/dom/WorkerPrivate.h" +#include "mozilla/dom/workers/ServiceWorkerManager.h" #include "mozilla/EventDispatcher.h" #include "mozilla/EventListenerManager.h" #include "mozilla/EventStateManager.h" @@ -267,6 +268,7 @@ bool nsContentUtils::sEncodeDecodeURLHash = false; bool nsContentUtils::sGettersDecodeURLHash = false; bool nsContentUtils::sPrivacyResistFingerprinting = false; bool nsContentUtils::sSendPerformanceTimingNotifications = false; +bool nsContentUtils::sSWInterceptionEnabled = false; uint32_t nsContentUtils::sHandlingInputTimeout = 1000; @@ -559,6 +561,10 @@ nsContentUtils::Init() Preferences::AddBoolVarCache(&sPrivacyResistFingerprinting, "privacy.resistFingerprinting", false); + Preferences::AddBoolVarCache(&sSWInterceptionEnabled, + "dom.serviceWorkers.interception.enabled", + false); + Preferences::AddUintVarCache(&sHandlingInputTimeout, "dom.event.handling-user-input-time-limit", 1000); @@ -1736,10 +1742,38 @@ nsContentUtils::ParseLegacyFontSize(const nsAString& aValue) return clamped(value, 1, 7); } +/* static */ +bool +nsContentUtils::IsControlledByServiceWorker(nsIDocument* aDocument) +{ + if (!ServiceWorkerInterceptionEnabled() || + nsContentUtils::IsInPrivateBrowsing(aDocument)) { + return false; + } + + nsRefPtr swm = + workers::ServiceWorkerManager::GetInstance(); + MOZ_ASSERT(swm); + + ErrorResult rv; + bool controlled = swm->IsControlled(aDocument, rv); + NS_WARN_IF(rv.Failed()); + + return !rv.Failed() && controlled; +} + /* static */ void nsContentUtils::GetOfflineAppManifest(nsIDocument *aDocument, nsIURI **aURI) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aDocument); + *aURI = nullptr; + + if (IsControlledByServiceWorker(aDocument)) { + return; + } + Element* docElement = aDocument->GetRootElement(); if (!docElement) { return; @@ -5230,7 +5264,6 @@ nsContentUtils::LeaveMicroTask() MOZ_ASSERT(NS_IsMainThread()); if (--sMicroTaskLevel == 0) { PerformMainThreadMicroTaskCheckpoint(); - nsDocument::ProcessBaseElementQueue(); } } diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index ba80c3b54e7..5182181b288 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -1955,6 +1955,14 @@ public: return sSendPerformanceTimingNotifications; } + /* + * Returns true if ServiceWorker Interception is enabled by pref. + */ + static bool ServiceWorkerInterceptionEnabled() + { + return sSWInterceptionEnabled; + } + /* * Returns true if the frame timing APIs are enabled. */ @@ -1995,6 +2003,11 @@ public: */ static bool HasPluginWithUncontrolledEventDispatch(nsIDocument* aDoc); + /** + * Return true if this doc is controlled by a ServiceWorker. + */ + static bool IsControlledByServiceWorker(nsIDocument* aDocument); + /** * Fire mutation events for changes caused by parsing directly into a * context node. @@ -2607,6 +2620,7 @@ private: static bool sGettersDecodeURLHash; static bool sPrivacyResistFingerprinting; static bool sSendPerformanceTimingNotifications; + static bool sSWInterceptionEnabled; static uint32_t sCookiesLifetimePolicy; static uint32_t sCookiesBehavior; diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index dc162816c04..578969b152f 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -5895,29 +5895,6 @@ nsDocument::RegisterUnresolvedElement(Element* aElement, nsIAtom* aTypeName) return NS_OK; } -namespace { - -class ProcessStackRunner final : public nsIRunnable -{ - ~ProcessStackRunner() {} -public: - explicit ProcessStackRunner(bool aIsBaseQueue = false) - : mIsBaseQueue(aIsBaseQueue) - { - } - NS_DECL_ISUPPORTS - NS_IMETHOD Run() override - { - nsDocument::ProcessTopElementQueue(mIsBaseQueue); - return NS_OK; - } - bool mIsBaseQueue; -}; - -NS_IMPL_ISUPPORTS(ProcessStackRunner, nsIRunnable); - -} // namespace - void nsDocument::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType, Element* aCustomElement, @@ -6019,10 +5996,7 @@ nsDocument::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType, // A new element queue needs to be pushed if the queue at the // top of the stack is associated with another microtask level. - // Don't push a queue for the level 0 microtask (base element queue) - // because we don't want to process the queue until the - // microtask checkpoint. - bool shouldPushElementQueue = nsContentUtils::MicroTaskLevel() > 0 && + bool shouldPushElementQueue = (!lastData || lastData->mAssociatedMicroTask < static_cast(nsContentUtils::MicroTaskLevel())); @@ -6045,39 +6019,22 @@ nsDocument::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType, // should be invoked prior to returning control back to script. // Create a script runner to process the top of the processing // stack as soon as it is safe to run script. - nsContentUtils::AddScriptRunner(new ProcessStackRunner()); + nsCOMPtr runnable = + NS_NewRunnableFunction(&nsDocument::ProcessTopElementQueue); + nsContentUtils::AddScriptRunner(runnable); } } } // static void -nsDocument::ProcessBaseElementQueue() -{ - // Prevent re-entrance. Also, if a microtask checkpoint is reached - // and there is no processing stack to process, then we are done. - if (sProcessingBaseElementQueue || !sProcessingStack) { - return; - } - - MOZ_ASSERT(nsContentUtils::MicroTaskLevel() == 0); - sProcessingBaseElementQueue = true; - nsContentUtils::AddScriptRunner(new ProcessStackRunner(true)); -} - -// static -void -nsDocument::ProcessTopElementQueue(bool aIsBaseQueue) +nsDocument::ProcessTopElementQueue() { MOZ_ASSERT(nsContentUtils::IsSafeToRunScript()); nsTArray>& stack = *sProcessingStack; uint32_t firstQueue = stack.LastIndexOf((CustomElementData*) nullptr); - if (aIsBaseQueue && firstQueue != 0) { - return; - } - for (uint32_t i = firstQueue + 1; i < stack.Length(); ++i) { // Callback queue may have already been processed in an earlier // element queue or in an element queue that was popped @@ -6095,7 +6052,6 @@ nsDocument::ProcessTopElementQueue(bool aIsBaseQueue) } else { // Don't pop sentinel for base element queue. stack.SetLength(1); - sProcessingBaseElementQueue = false; } } @@ -6111,10 +6067,6 @@ nsDocument::RegisterEnabled() Maybe>> nsDocument::sProcessingStack; -// static -bool -nsDocument::sProcessingBaseElementQueue; - void nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType, const ElementRegistrationOptions& aOptions, @@ -11734,6 +11686,41 @@ GetRootWindow(nsIDocument* aDoc) return rootItem ? rootItem->GetWindow() : nullptr; } +static bool +ShouldApplyFullscreenDirectly(nsIDocument* aDoc, + nsPIDOMWindow* aRootWin) +{ + if (XRE_GetProcessType() == GeckoProcessType_Content) { + // If we are in the content process, we can apply the fullscreen + // state directly only if we have been in DOM fullscreen, because + // otherwise we always need to notify the chrome. + return nsContentUtils::GetRootDocument(aDoc)->IsFullScreenDoc(); + } else { + // If we are in the chrome process, and the window has not been in + // fullscreen, we certainly need to make that fullscreen first. + bool fullscreen; + NS_WARN_IF(NS_FAILED(aRootWin->GetFullScreen(&fullscreen))); + if (!fullscreen) { + return false; + } + // The iterator not being at end indicates there is still some + // pending fullscreen request relates to this document. We have to + // push the request to the pending queue so requests are handled + // in the correct order. + PendingFullscreenRequestList::Iterator + iter(aDoc, PendingFullscreenRequestList::eDocumentsWithSameRoot); + if (!iter.AtEnd()) { + return false; + } + // We have to apply the fullscreen state directly in this case, + // because nsGlobalWindow::SetFullscreenInternal() will do nothing + // if it is already in fullscreen. If we do not apply the state but + // instead add it to the queue and wait for the window as normal, + // we would get stuck. + return true; + } +} + void nsDocument::RequestFullScreen(UniquePtr&& aRequest) { @@ -11742,19 +11729,7 @@ nsDocument::RequestFullScreen(UniquePtr&& aRequest) return; } - // If we have been in fullscreen, apply the new state directly. - // Note that we should check both condition, because if we are in - // child process, our window may not report to be in fullscreen. - // Also, it is possible that the root window reports that it is in - // fullscreen while there exists pending fullscreen request because - // of ongoing fullscreen transition. In that case, we shouldn't - // apply the state before any previous request. - if ((static_cast(rootWin.get())->FullScreen() && - // The iterator being at end at the beginning indicates there is - // no pending fullscreen request which relates to this document. - PendingFullscreenRequestList::Iterator( - this, PendingFullscreenRequestList::eDocumentsWithSameRoot).AtEnd()) || - nsContentUtils::GetRootDocument(this)->IsFullScreenDoc()) { + if (ShouldApplyFullscreenDirectly(this, rootWin)) { ApplyFullscreen(*aRequest); return; } @@ -13043,6 +13018,24 @@ nsDocument::ReportUseCounters() // that feature were removed. Whereas with a document/top-level // page split, we can see that N documents would be affected, but // only a single web page would be affected. + + // The difference between the values of these two histograms and the + // related use counters below tell us how many pages did *not* use + // the feature in question. For instance, if we see that a given + // session has destroyed 30 content documents, but a particular use + // counter shows only a count of 5, we can infer that the use + // counter was *not* used in 25 of those 30 documents. + // + // We do things this way, rather than accumulating a boolean flag + // for each use counter, to avoid sending histograms for features + // that don't get widely used. Doing things in this fashion means + // smaller telemetry payloads and faster processing on the server + // side. + Telemetry::Accumulate(Telemetry::CONTENT_DOCUMENTS_DESTROYED, 1); + if (IsTopLevelContentDocument()) { + Telemetry::Accumulate(Telemetry::TOP_LEVEL_CONTENT_DOCUMENTS_DESTROYED, 1); + } + for (int32_t c = 0; c < eUseCounter_Count; ++c) { UseCounter uc = static_cast(c); @@ -13051,24 +13044,8 @@ nsDocument::ReportUseCounters() static_cast(Telemetry::HistogramFirstUseCounter + uc * 2); bool value = GetUseCounter(uc); - if (sDebugUseCounters && value) { - const char* name = Telemetry::GetHistogramName(id); - if (name) { - printf(" %s", name); - } else { - printf(" #%d", id); - } - printf(": %d\n", value); - } - - Telemetry::Accumulate(id, value); - - if (IsTopLevelContentDocument()) { - id = static_cast(Telemetry::HistogramFirstUseCounter + - uc * 2 + 1); - value = GetUseCounter(uc) || GetChildDocumentUseCounter(uc); - - if (sDebugUseCounters && value) { + if (value) { + if (sDebugUseCounters) { const char* name = Telemetry::GetHistogramName(id); if (name) { printf(" %s", name); @@ -13078,7 +13055,27 @@ nsDocument::ReportUseCounters() printf(": %d\n", value); } - Telemetry::Accumulate(id, value); + Telemetry::Accumulate(id, 1); + } + + if (IsTopLevelContentDocument()) { + id = static_cast(Telemetry::HistogramFirstUseCounter + + uc * 2 + 1); + value = GetUseCounter(uc) || GetChildDocumentUseCounter(uc); + + if (value) { + if (sDebugUseCounters) { + const char* name = Telemetry::GetHistogramName(id); + if (name) { + printf(" %s", name); + } else { + printf(" #%d", id); + } + printf(": %d\n", value); + } + + Telemetry::Accumulate(id, 1); + } } } } diff --git a/dom/base/nsDocument.h b/dom/base/nsDocument.h index 08132843421..5606647ce36 100644 --- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -1303,7 +1303,7 @@ public: mozilla::dom::LifecycleCallbackArgs* aArgs = nullptr, mozilla::dom::CustomElementDefinition* aDefinition = nullptr) override; - static void ProcessTopElementQueue(bool aIsBaseQueue = false); + static void ProcessTopElementQueue(); void GetCustomPrototype(int32_t aNamespaceID, nsIAtom* aAtom, @@ -1600,15 +1600,9 @@ private: // queue in the stack is the base element queue. static mozilla::Maybe>> sProcessingStack; - // Flag to prevent re-entrance into base element queue as described in the - // custom elements speicification. - static bool sProcessingBaseElementQueue; - static bool CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp); public: - static void ProcessBaseElementQueue(); - // Enqueue created callback or register upgrade candidate for // newly created custom elements, possibly extending an existing type. // ex. ,