summaryrefslogtreecommitdiffstats
path: root/dom/base/nsWindowRoot.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/base/nsWindowRoot.cpp392
1 files changed, 392 insertions, 0 deletions
diff --git a/dom/base/nsWindowRoot.cpp b/dom/base/nsWindowRoot.cpp
new file mode 100644
index 0000000000..1ee2368f1e
--- /dev/null
+++ b/dom/base/nsWindowRoot.cpp
@@ -0,0 +1,392 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "mozilla/BasicEvents.h"
+#include "mozilla/EventDispatcher.h"
+#include "mozilla/EventListenerManager.h"
+#include "mozilla/StaticPrefs_browser.h"
+#include "mozilla/dom/WindowRootBinding.h"
+#include "nsCOMPtr.h"
+#include "nsWindowRoot.h"
+#include "nsPIDOMWindow.h"
+#include "nsPresContext.h"
+#include "nsLayoutCID.h"
+#include "nsContentCID.h"
+#include "nsString.h"
+#include "nsFrameLoaderOwner.h"
+#include "nsFrameLoader.h"
+#include "nsQueryActor.h"
+#include "nsGlobalWindowOuter.h"
+#include "nsFocusManager.h"
+#include "nsIContent.h"
+#include "nsIControllers.h"
+#include "nsIController.h"
+#include "nsQueryObject.h"
+#include "xpcpublic.h"
+#include "nsCycleCollectionParticipant.h"
+#include "mozilla/dom/BrowserParent.h"
+#include "mozilla/dom/CanonicalBrowsingContext.h"
+#include "mozilla/dom/HTMLTextAreaElement.h"
+#include "mozilla/dom/HTMLInputElement.h"
+#include "mozilla/dom/JSActorService.h"
+#include "mozilla/dom/WindowGlobalParent.h"
+
+#include "nsXULElement.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+nsWindowRoot::nsWindowRoot(nsPIDOMWindowOuter* aWindow) {
+ SetIsOnMainThread();
+ mWindow = aWindow;
+}
+
+nsWindowRoot::~nsWindowRoot() {
+ if (mListenerManager) {
+ mListenerManager->Disconnect();
+ }
+
+ JSActorService::UnregisterChromeEventTarget(this);
+}
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(nsWindowRoot)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsWindowRoot)
+ JSActorService::UnregisterChromeEventTarget(tmp);
+
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsWindowRoot)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsWindowRoot)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+ NS_INTERFACE_MAP_ENTRY(nsPIWindowRoot)
+ NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsWindowRoot)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsWindowRoot)
+
+bool nsWindowRoot::DispatchEvent(Event& aEvent, CallerType aCallerType,
+ ErrorResult& aRv) {
+ nsEventStatus status = nsEventStatus_eIgnore;
+ nsresult rv = EventDispatcher::DispatchDOMEvent(
+ static_cast<EventTarget*>(this), nullptr, &aEvent, nullptr, &status);
+ bool retval = !aEvent.DefaultPrevented(aCallerType);
+ if (NS_FAILED(rv)) {
+ aRv.Throw(rv);
+ }
+ return retval;
+}
+
+bool nsWindowRoot::ComputeDefaultWantsUntrusted(ErrorResult& aRv) {
+ return false;
+}
+
+EventListenerManager* nsWindowRoot::GetOrCreateListenerManager() {
+ if (!mListenerManager) {
+ mListenerManager =
+ new EventListenerManager(static_cast<EventTarget*>(this));
+ }
+
+ return mListenerManager;
+}
+
+EventListenerManager* nsWindowRoot::GetExistingListenerManager() const {
+ return mListenerManager;
+}
+
+void nsWindowRoot::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
+ aVisitor.mCanHandle = true;
+ aVisitor.mForceContentDispatch = true; // FIXME! Bug 329119
+ // To keep mWindow alive
+ aVisitor.mItemData = static_cast<nsISupports*>(mWindow);
+ aVisitor.SetParentTarget(mParent, false);
+}
+
+nsresult nsWindowRoot::PostHandleEvent(EventChainPostVisitor& aVisitor) {
+ return NS_OK;
+}
+
+nsPIDOMWindowOuter* nsWindowRoot::GetOwnerGlobalForBindingsInternal() {
+ return mWindow;
+}
+
+nsIGlobalObject* nsWindowRoot::GetOwnerGlobal() const {
+ nsCOMPtr<nsIGlobalObject> global =
+ do_QueryInterface(mWindow->GetCurrentInnerWindow());
+ // We're still holding a ref to it, so returning the raw pointer is ok...
+ return global;
+}
+
+nsPIDOMWindowOuter* nsWindowRoot::GetWindow() { return mWindow; }
+
+nsresult nsWindowRoot::GetControllers(bool aForVisibleWindow,
+ nsIControllers** aResult) {
+ *aResult = nullptr;
+
+ // XXX: we should fix this so there's a generic interface that
+ // describes controllers, so this code would have no special
+ // knowledge of what object might have controllers.
+
+ nsFocusManager::SearchRange searchRange =
+ aForVisibleWindow ? nsFocusManager::eIncludeVisibleDescendants
+ : nsFocusManager::eIncludeAllDescendants;
+ nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
+ nsIContent* focusedContent = nsFocusManager::GetFocusedDescendant(
+ mWindow, searchRange, getter_AddRefs(focusedWindow));
+ if (focusedContent) {
+ RefPtr<nsXULElement> xulElement = nsXULElement::FromNode(focusedContent);
+ if (xulElement) {
+ ErrorResult rv;
+ *aResult = xulElement->GetControllers(rv);
+ NS_IF_ADDREF(*aResult);
+ return rv.StealNSResult();
+ }
+
+ HTMLTextAreaElement* htmlTextArea =
+ HTMLTextAreaElement::FromNode(focusedContent);
+ if (htmlTextArea) return htmlTextArea->GetControllers(aResult);
+
+ HTMLInputElement* htmlInputElement =
+ HTMLInputElement::FromNode(focusedContent);
+ if (htmlInputElement) return htmlInputElement->GetControllers(aResult);
+
+ if (focusedContent->IsEditable() && focusedWindow)
+ return focusedWindow->GetControllers(aResult);
+ } else {
+ return focusedWindow->GetControllers(aResult);
+ }
+
+ return NS_OK;
+}
+
+nsresult nsWindowRoot::GetControllerForCommand(const char* aCommand,
+ bool aForVisibleWindow,
+ nsIController** _retval) {
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = nullptr;
+
+ // If this is the parent process, check if a child browsing context from
+ // another process is focused, and ask if it has a controller actor that
+ // supports the command.
+ if (XRE_IsParentProcess()) {
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (!fm) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Unfortunately, messages updating the active/focus state in the focus
+ // manager don't happen fast enough in the case when switching focus between
+ // processes when clicking on a chrome UI element while a child tab is
+ // focused, so we need to check whether the focus manager thinks a child
+ // frame is focused as well.
+ nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
+ nsIContent* focusedContent = nsFocusManager::GetFocusedDescendant(
+ mWindow, nsFocusManager::eIncludeAllDescendants,
+ getter_AddRefs(focusedWindow));
+ RefPtr<nsFrameLoaderOwner> loaderOwner = do_QueryObject(focusedContent);
+ if (loaderOwner) {
+ // Only check browsing contexts if a remote frame is focused. If chrome is
+ // focused, just check the controllers directly below.
+ RefPtr<nsFrameLoader> frameLoader = loaderOwner->GetFrameLoader();
+ if (frameLoader && frameLoader->IsRemoteFrame()) {
+ // GetActiveBrowsingContextInChrome actually returns the top-level
+ // browsing context if the focus is in a child process tab, or null if
+ // the focus is in chrome.
+ BrowsingContext* focusedBC =
+ fm->GetActiveBrowsingContextInChrome()
+ ? fm->GetFocusedBrowsingContextInChrome()
+ : nullptr;
+ if (focusedBC) {
+ // At this point, it is known that a child process is focused, so ask
+ // its Controllers actor if the command is supported.
+ nsCOMPtr<nsIController> controller = do_QueryActor(
+ "Controllers", focusedBC->Canonical()->GetCurrentWindowGlobal());
+ if (controller) {
+ bool supported;
+ controller->SupportsCommand(aCommand, &supported);
+ if (supported) {
+ controller.forget(_retval);
+ return NS_OK;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ {
+ nsCOMPtr<nsIControllers> controllers;
+ GetControllers(aForVisibleWindow, getter_AddRefs(controllers));
+ if (controllers) {
+ nsCOMPtr<nsIController> controller;
+ controllers->GetControllerForCommand(aCommand,
+ getter_AddRefs(controller));
+ if (controller) {
+ controller.forget(_retval);
+ return NS_OK;
+ }
+ }
+ }
+
+ nsFocusManager::SearchRange searchRange =
+ aForVisibleWindow ? nsFocusManager::eIncludeVisibleDescendants
+ : nsFocusManager::eIncludeAllDescendants;
+ nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
+ nsFocusManager::GetFocusedDescendant(mWindow, searchRange,
+ getter_AddRefs(focusedWindow));
+ while (focusedWindow) {
+ nsCOMPtr<nsIControllers> controllers;
+ focusedWindow->GetControllers(getter_AddRefs(controllers));
+ if (controllers) {
+ nsCOMPtr<nsIController> controller;
+ controllers->GetControllerForCommand(aCommand,
+ getter_AddRefs(controller));
+ if (controller) {
+ controller.forget(_retval);
+ return NS_OK;
+ }
+ }
+
+ // XXXndeakin P3 is this casting safe?
+ nsGlobalWindowOuter* win = nsGlobalWindowOuter::Cast(focusedWindow);
+ focusedWindow = win->GetPrivateParent();
+ }
+
+ return NS_OK;
+}
+
+void nsWindowRoot::GetEnabledDisabledCommandsForControllers(
+ nsIControllers* aControllers, nsTHashSet<nsCString>& aCommandsHandled,
+ nsTArray<nsCString>& aEnabledCommands,
+ nsTArray<nsCString>& aDisabledCommands) {
+ uint32_t controllerCount;
+ aControllers->GetControllerCount(&controllerCount);
+ for (uint32_t c = 0; c < controllerCount; c++) {
+ nsCOMPtr<nsIController> controller;
+ aControllers->GetControllerAt(c, getter_AddRefs(controller));
+
+ nsCOMPtr<nsICommandController> commandController(
+ do_QueryInterface(controller));
+ if (commandController) {
+ // All of our default command controllers have 20-60 commands. Let's just
+ // leave enough space here for all of them so we probably don't need to
+ // heap-allocate.
+ AutoTArray<nsCString, 64> commands;
+ if (NS_SUCCEEDED(commandController->GetSupportedCommands(commands))) {
+ for (auto& commandStr : commands) {
+ // Use a hash to determine which commands have already been handled by
+ // earlier controllers, as the earlier controller's result should get
+ // priority.
+ if (aCommandsHandled.EnsureInserted(commandStr)) {
+ // We inserted a new entry into aCommandsHandled.
+ bool enabled = false;
+ controller->IsCommandEnabled(commandStr.get(), &enabled);
+
+ if (enabled) {
+ aEnabledCommands.AppendElement(commandStr);
+ } else {
+ aDisabledCommands.AppendElement(commandStr);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void nsWindowRoot::GetEnabledDisabledCommands(
+ nsTArray<nsCString>& aEnabledCommands,
+ nsTArray<nsCString>& aDisabledCommands) {
+ nsTHashSet<nsCString> commandsHandled;
+
+ nsCOMPtr<nsIControllers> controllers;
+ GetControllers(false, getter_AddRefs(controllers));
+ if (controllers) {
+ GetEnabledDisabledCommandsForControllers(
+ controllers, commandsHandled, aEnabledCommands, aDisabledCommands);
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
+ nsFocusManager::GetFocusedDescendant(mWindow,
+ nsFocusManager::eIncludeAllDescendants,
+ getter_AddRefs(focusedWindow));
+ while (focusedWindow) {
+ focusedWindow->GetControllers(getter_AddRefs(controllers));
+ if (controllers) {
+ GetEnabledDisabledCommandsForControllers(
+ controllers, commandsHandled, aEnabledCommands, aDisabledCommands);
+ }
+
+ nsGlobalWindowOuter* win = nsGlobalWindowOuter::Cast(focusedWindow);
+ focusedWindow = win->GetPrivateParent();
+ }
+}
+
+already_AddRefed<nsINode> nsWindowRoot::GetPopupNode() {
+ nsCOMPtr<nsINode> popupNode = do_QueryReferent(mPopupNode);
+ return popupNode.forget();
+}
+
+void nsWindowRoot::SetPopupNode(nsINode* aNode) {
+ mPopupNode = do_GetWeakReference(aNode);
+}
+
+nsIGlobalObject* nsWindowRoot::GetParentObject() {
+ return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
+}
+
+JSObject* nsWindowRoot::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ return mozilla::dom::WindowRoot_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+void nsWindowRoot::AddBrowser(nsIRemoteTab* aBrowser) {
+ nsWeakPtr weakBrowser = do_GetWeakReference(aBrowser);
+ mWeakBrowsers.Insert(weakBrowser);
+}
+
+void nsWindowRoot::RemoveBrowser(nsIRemoteTab* aBrowser) {
+ nsWeakPtr weakBrowser = do_GetWeakReference(aBrowser);
+ mWeakBrowsers.Remove(weakBrowser);
+}
+
+void nsWindowRoot::EnumerateBrowsers(BrowserEnumerator aEnumFunc, void* aArg) {
+ // Collect strong references to all browsers in a separate array in
+ // case aEnumFunc alters mWeakBrowsers.
+ nsTArray<nsCOMPtr<nsIRemoteTab>> remoteTabs;
+ for (const auto& key : mWeakBrowsers) {
+ nsCOMPtr<nsIRemoteTab> remoteTab(do_QueryReferent(key));
+ if (remoteTab) {
+ remoteTabs.AppendElement(remoteTab);
+ }
+ }
+
+ for (uint32_t i = 0; i < remoteTabs.Length(); ++i) {
+ aEnumFunc(remoteTabs[i], aArg);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////
+
+already_AddRefed<EventTarget> NS_NewWindowRoot(nsPIDOMWindowOuter* aWindow) {
+ nsCOMPtr<EventTarget> result = new nsWindowRoot(aWindow);
+
+ RefPtr<JSActorService> wasvc = JSActorService::GetSingleton();
+ wasvc->RegisterChromeEventTarget(result);
+
+ return result.forget();
+}