diff options
Diffstat (limited to 'embedding/browser/nsDocShellTreeOwner.cpp')
-rw-r--r-- | embedding/browser/nsDocShellTreeOwner.cpp | 1674 |
1 files changed, 1674 insertions, 0 deletions
diff --git a/embedding/browser/nsDocShellTreeOwner.cpp b/embedding/browser/nsDocShellTreeOwner.cpp new file mode 100644 index 000000000..73397cc8b --- /dev/null +++ b/embedding/browser/nsDocShellTreeOwner.cpp @@ -0,0 +1,1674 @@ +/* -*- 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/. */ + +// Local Includes +#include "nsDocShellTreeOwner.h" +#include "nsWebBrowser.h" + +// Helper Classes +#include "nsStyleCoord.h" +#include "nsSize.h" +#include "mozilla/ReflowInput.h" +#include "nsIServiceManager.h" +#include "nsComponentManagerUtils.h" +#include "nsXPIDLString.h" +#include "nsIAtom.h" +#include "nsReadableUtils.h" +#include "nsUnicharUtils.h" +#include "nsISimpleEnumerator.h" +#include "mozilla/LookAndFeel.h" + +// Interfaces needed to be included +#include "nsPresContext.h" +#include "nsIContextMenuListener.h" +#include "nsIContextMenuListener2.h" +#include "nsITooltipListener.h" +#include "nsIDOMNode.h" +#include "nsIDOMNodeList.h" +#include "nsIDOMDocument.h" +#include "nsIDOMDocumentType.h" +#include "nsIDOMElement.h" +#include "Link.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/SVGTitleElement.h" +#include "nsIDOMEvent.h" +#include "nsIDOMFileList.h" +#include "nsIDOMMouseEvent.h" +#include "nsIFormControl.h" +#include "nsIDOMHTMLInputElement.h" +#include "nsIDOMHTMLTextAreaElement.h" +#include "nsIDOMHTMLHtmlElement.h" +#include "nsIDOMHTMLAppletElement.h" +#include "nsIDOMHTMLObjectElement.h" +#include "nsIDOMHTMLEmbedElement.h" +#include "nsIDOMHTMLDocument.h" +#include "nsIImageLoadingContent.h" +#include "nsIWebNavigation.h" +#include "nsIDOMHTMLElement.h" +#include "nsIPresShell.h" +#include "nsIStringBundle.h" +#include "nsPIDOMWindow.h" +#include "nsPIWindowRoot.h" +#include "nsIDOMWindowCollection.h" +#include "nsIWindowWatcher.h" +#include "nsPIWindowWatcher.h" +#include "nsIPrompt.h" +#include "nsITabParent.h" +#include "nsITabChild.h" +#include "nsRect.h" +#include "nsIWebBrowserChromeFocus.h" +#include "nsIContent.h" +#include "imgIContainer.h" +#include "nsContextMenuInfo.h" +#include "nsPresContext.h" +#include "nsViewManager.h" +#include "nsView.h" +#include "nsIDOMDragEvent.h" +#include "nsIConstraintValidation.h" +#include "mozilla/Attributes.h" +#include "mozilla/EventListenerManager.h" +#include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent() +#include "mozilla/dom/File.h" // for input type=file +#include "mozilla/dom/FileList.h" // for input type=file + +using namespace mozilla; +using namespace mozilla::dom; + +// A helper routine that navigates the tricky path from a |nsWebBrowser| to +// a |EventTarget| via the window root and chrome event handler. +static nsresult +GetDOMEventTarget(nsWebBrowser* aInBrowser, EventTarget** aTarget) +{ + if (!aInBrowser) { + return NS_ERROR_INVALID_POINTER; + } + + nsCOMPtr<mozIDOMWindowProxy> domWindow; + aInBrowser->GetContentDOMWindow(getter_AddRefs(domWindow)); + if (!domWindow) { + return NS_ERROR_FAILURE; + } + + auto* outerWindow = nsPIDOMWindowOuter::From(domWindow); + nsPIDOMWindowOuter* rootWindow = outerWindow->GetPrivateRoot(); + NS_ENSURE_TRUE(rootWindow, NS_ERROR_FAILURE); + nsCOMPtr<EventTarget> target = rootWindow->GetChromeEventHandler(); + NS_ENSURE_TRUE(target, NS_ERROR_FAILURE); + target.forget(aTarget); + + return NS_OK; +} + +nsDocShellTreeOwner::nsDocShellTreeOwner() + : mWebBrowser(nullptr) + , mTreeOwner(nullptr) + , mPrimaryContentShell(nullptr) + , mWebBrowserChrome(nullptr) + , mOwnerWin(nullptr) + , mOwnerRequestor(nullptr) +{ +} + +nsDocShellTreeOwner::~nsDocShellTreeOwner() +{ + RemoveChromeListeners(); +} + +NS_IMPL_ADDREF(nsDocShellTreeOwner) +NS_IMPL_RELEASE(nsDocShellTreeOwner) + +NS_INTERFACE_MAP_BEGIN(nsDocShellTreeOwner) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocShellTreeOwner) + NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeOwner) + NS_INTERFACE_MAP_ENTRY(nsIBaseWindow) + NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) + NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener) + NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener) + NS_INTERFACE_MAP_ENTRY(nsICDocShellTreeOwner) + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) +NS_INTERFACE_MAP_END + +//***************************************************************************** +// nsDocShellTreeOwner::nsIInterfaceRequestor +//***************************************************************************** + +NS_IMETHODIMP +nsDocShellTreeOwner::GetInterface(const nsIID& aIID, void** aSink) +{ + NS_ENSURE_ARG_POINTER(aSink); + + if (NS_SUCCEEDED(QueryInterface(aIID, aSink))) { + return NS_OK; + } + + if (aIID.Equals(NS_GET_IID(nsIWebBrowserChromeFocus))) { + if (mWebBrowserChromeWeak != nullptr) { + return mWebBrowserChromeWeak->QueryReferent(aIID, aSink); + } + return mOwnerWin->QueryInterface(aIID, aSink); + } + + if (aIID.Equals(NS_GET_IID(nsIPrompt))) { + nsCOMPtr<nsIPrompt> prompt; + EnsurePrompter(); + prompt = mPrompter; + if (prompt) { + prompt.forget(aSink); + return NS_OK; + } + return NS_NOINTERFACE; + } + + if (aIID.Equals(NS_GET_IID(nsIAuthPrompt))) { + nsCOMPtr<nsIAuthPrompt> prompt; + EnsureAuthPrompter(); + prompt = mAuthPrompter; + if (prompt) { + prompt.forget(aSink); + return NS_OK; + } + return NS_NOINTERFACE; + } + + nsCOMPtr<nsIInterfaceRequestor> req = GetOwnerRequestor(); + if (req) { + return req->GetInterface(aIID, aSink); + } + + return NS_NOINTERFACE; +} + +//***************************************************************************** +// nsDocShellTreeOwner::nsIDocShellTreeOwner +//***************************************************************************** + +void +nsDocShellTreeOwner::EnsurePrompter() +{ + if (mPrompter) { + return; + } + + nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID)); + if (wwatch && mWebBrowser) { + nsCOMPtr<mozIDOMWindowProxy> domWindow; + mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow)); + if (domWindow) { + wwatch->GetNewPrompter(domWindow, getter_AddRefs(mPrompter)); + } + } +} + +void +nsDocShellTreeOwner::EnsureAuthPrompter() +{ + if (mAuthPrompter) { + return; + } + + nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID)); + if (wwatch && mWebBrowser) { + nsCOMPtr<mozIDOMWindowProxy> domWindow; + mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow)); + if (domWindow) { + wwatch->GetNewAuthPrompter(domWindow, getter_AddRefs(mAuthPrompter)); + } + } +} + +void +nsDocShellTreeOwner::AddToWatcher() +{ + if (mWebBrowser) { + nsCOMPtr<mozIDOMWindowProxy> domWindow; + mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow)); + if (domWindow) { + nsCOMPtr<nsPIWindowWatcher> wwatch( + do_GetService(NS_WINDOWWATCHER_CONTRACTID)); + if (wwatch) { + nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome(); + if (webBrowserChrome) { + wwatch->AddWindow(domWindow, webBrowserChrome); + } + } + } + } +} + +void +nsDocShellTreeOwner::RemoveFromWatcher() +{ + if (mWebBrowser) { + nsCOMPtr<mozIDOMWindowProxy> domWindow; + mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow)); + if (domWindow) { + nsCOMPtr<nsPIWindowWatcher> wwatch( + do_GetService(NS_WINDOWWATCHER_CONTRACTID)); + if (wwatch) { + wwatch->RemoveWindow(domWindow); + } + } + } +} + +void +nsDocShellTreeOwner::EnsureContentTreeOwner() +{ + if (mContentTreeOwner) { + return; + } + + mContentTreeOwner = new nsDocShellTreeOwner(); + nsCOMPtr<nsIWebBrowserChrome> browserChrome = GetWebBrowserChrome(); + if (browserChrome) { + mContentTreeOwner->SetWebBrowserChrome(browserChrome); + } + + if (mWebBrowser) { + mContentTreeOwner->WebBrowser(mWebBrowser); + } +} + +NS_IMETHODIMP +nsDocShellTreeOwner::ContentShellAdded(nsIDocShellTreeItem* aContentShell, + bool aPrimary, bool aTargetable, + const nsAString& aID) +{ + if (mTreeOwner) + return mTreeOwner->ContentShellAdded(aContentShell, aPrimary, aTargetable, + aID); + + EnsureContentTreeOwner(); + aContentShell->SetTreeOwner(mContentTreeOwner); + + if (aPrimary) { + mPrimaryContentShell = aContentShell; + mPrimaryTabParent = nullptr; + } + return NS_OK; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::ContentShellRemoved(nsIDocShellTreeItem* aContentShell) +{ + if (mTreeOwner) { + return mTreeOwner->ContentShellRemoved(aContentShell); + } + + if (mPrimaryContentShell == aContentShell) { + mPrimaryContentShell = nullptr; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::GetPrimaryContentShell(nsIDocShellTreeItem** aShell) +{ + NS_ENSURE_ARG_POINTER(aShell); + + if (mTreeOwner) { + return mTreeOwner->GetPrimaryContentShell(aShell); + } + + nsCOMPtr<nsIDocShellTreeItem> shell; + if (!mPrimaryTabParent) { + shell = + mPrimaryContentShell ? mPrimaryContentShell : mWebBrowser->mDocShell; + } + shell.forget(aShell); + + return NS_OK; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::TabParentAdded(nsITabParent* aTab, bool aPrimary) +{ + if (mTreeOwner) { + return mTreeOwner->TabParentAdded(aTab, aPrimary); + } + + if (aPrimary) { + mPrimaryTabParent = aTab; + mPrimaryContentShell = nullptr; + } else if (mPrimaryTabParent == aTab) { + mPrimaryTabParent = nullptr; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::TabParentRemoved(nsITabParent* aTab) +{ + if (mTreeOwner) { + return mTreeOwner->TabParentRemoved(aTab); + } + + if (aTab == mPrimaryTabParent) { + mPrimaryTabParent = nullptr; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::GetPrimaryTabParent(nsITabParent** aTab) +{ + if (mTreeOwner) { + return mTreeOwner->GetPrimaryTabParent(aTab); + } + + nsCOMPtr<nsITabParent> tab = mPrimaryTabParent; + tab.forget(aTab); + return NS_OK; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::GetPrimaryContentSize(int32_t* aWidth, + int32_t* aHeight) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::SetPrimaryContentSize(int32_t aWidth, + int32_t aHeight) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::GetRootShellSize(int32_t* aWidth, + int32_t* aHeight) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::SetRootShellSize(int32_t aWidth, + int32_t aHeight) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::SizeShellTo(nsIDocShellTreeItem* aShellItem, + int32_t aCX, int32_t aCY) +{ + nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome(); + + NS_ENSURE_STATE(mTreeOwner || webBrowserChrome); + + if (mTreeOwner) { + return mTreeOwner->SizeShellTo(aShellItem, aCX, aCY); + } + + if (aShellItem == mWebBrowser->mDocShell) { + nsCOMPtr<nsITabChild> tabChild = do_QueryInterface(webBrowserChrome); + if (tabChild) { + // The XUL window to resize is in the parent process, but there we + // won't be able to get aShellItem to do the hack in nsXULWindow::SizeShellTo, + // so let's send the width and height of aShellItem too. + nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(aShellItem)); + NS_ENSURE_TRUE(shellAsWin, NS_ERROR_FAILURE); + + int32_t width = 0; + int32_t height = 0; + shellAsWin->GetSize(&width, &height); + return tabChild->RemoteSizeShellTo(aCX, aCY, width, height); + } + return webBrowserChrome->SizeBrowserTo(aCX, aCY); + } + + nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(aShellItem)); + NS_ENSURE_TRUE(webNav, NS_ERROR_FAILURE); + + nsCOMPtr<nsIDOMDocument> domDocument; + webNav->GetDocument(getter_AddRefs(domDocument)); + NS_ENSURE_TRUE(domDocument, NS_ERROR_FAILURE); + + nsCOMPtr<nsIDOMElement> domElement; + domDocument->GetDocumentElement(getter_AddRefs(domElement)); + NS_ENSURE_TRUE(domElement, NS_ERROR_FAILURE); + + // Set the preferred Size + //XXX + NS_ERROR("Implement this"); + /* + Set the preferred size on the aShellItem. + */ + + RefPtr<nsPresContext> presContext; + mWebBrowser->mDocShell->GetPresContext(getter_AddRefs(presContext)); + NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE); + + nsIPresShell* presShell = presContext->GetPresShell(); + NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE); + + NS_ENSURE_SUCCESS( + presShell->ResizeReflow(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE), + NS_ERROR_FAILURE); + + nsRect shellArea = presContext->GetVisibleArea(); + + int32_t browserCX = presContext->AppUnitsToDevPixels(shellArea.width); + int32_t browserCY = presContext->AppUnitsToDevPixels(shellArea.height); + + return webBrowserChrome->SizeBrowserTo(browserCX, browserCY); +} + +NS_IMETHODIMP +nsDocShellTreeOwner::SetPersistence(bool aPersistPosition, + bool aPersistSize, + bool aPersistSizeMode) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::GetPersistence(bool* aPersistPosition, + bool* aPersistSize, + bool* aPersistSizeMode) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::GetTargetableShellCount(uint32_t* aResult) +{ + if (mTreeOwner) { + mTreeOwner->GetTargetableShellCount(aResult); + } else { + *aResult = 0; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::GetHasPrimaryContent(bool* aResult) +{ + *aResult = mPrimaryTabParent || mPrimaryContentShell; + return NS_OK; +} + +//***************************************************************************** +// nsDocShellTreeOwner::nsIBaseWindow +//***************************************************************************** + +NS_IMETHODIMP +nsDocShellTreeOwner::InitWindow(nativeWindow aParentNativeWindow, + nsIWidget* aParentWidget, int32_t aX, + int32_t aY, int32_t aCX, int32_t aCY) +{ + return NS_ERROR_NULL_POINTER; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::Create() +{ + return NS_ERROR_NULL_POINTER; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::Destroy() +{ + nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome(); + if (webBrowserChrome) { + return webBrowserChrome->DestroyBrowserWindow(); + } + + return NS_ERROR_NULL_POINTER; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::GetUnscaledDevicePixelsPerCSSPixel(double* aScale) +{ + if (mWebBrowser) { + return mWebBrowser->GetUnscaledDevicePixelsPerCSSPixel(aScale); + } + + *aScale = 1.0; + return NS_OK; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::GetDevicePixelsPerDesktopPixel(double* aScale) +{ + if (mWebBrowser) { + return mWebBrowser->GetDevicePixelsPerDesktopPixel(aScale); + } + + *aScale = 1.0; + return NS_OK; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::SetPositionDesktopPix(int32_t aX, int32_t aY) +{ + if (mWebBrowser) { + nsresult rv = mWebBrowser->SetPositionDesktopPix(aX, aY); + NS_ENSURE_SUCCESS(rv, rv); + } + + double scale = 1.0; + GetDevicePixelsPerDesktopPixel(&scale); + return SetPosition(NSToIntRound(aX * scale), NSToIntRound(aY * scale)); +} + +NS_IMETHODIMP +nsDocShellTreeOwner::SetPosition(int32_t aX, int32_t aY) +{ + nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin(); + if (ownerWin) { + return ownerWin->SetDimensions(nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION, + aX, aY, 0, 0); + } + return NS_ERROR_NULL_POINTER; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::GetPosition(int32_t* aX, int32_t* aY) +{ + nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin(); + if (ownerWin) { + return ownerWin->GetDimensions(nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION, + aX, aY, nullptr, nullptr); + } + return NS_ERROR_NULL_POINTER; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::SetSize(int32_t aCX, int32_t aCY, bool aRepaint) +{ + nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin(); + if (ownerWin) { + return ownerWin->SetDimensions(nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_OUTER, + 0, 0, aCX, aCY); + } + return NS_ERROR_NULL_POINTER; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::GetSize(int32_t* aCX, int32_t* aCY) +{ + nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin(); + if (ownerWin) { + return ownerWin->GetDimensions(nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_OUTER, + nullptr, nullptr, aCX, aCY); + } + return NS_ERROR_NULL_POINTER; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::SetPositionAndSize(int32_t aX, int32_t aY, int32_t aCX, + int32_t aCY, uint32_t aFlags) +{ + nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin(); + if (ownerWin) { + return ownerWin->SetDimensions( + nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_OUTER | + nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION, + aX, aY, aCX, aCY); + } + return NS_ERROR_NULL_POINTER; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::GetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aCX, + int32_t* aCY) +{ + nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin(); + if (ownerWin) { + return ownerWin->GetDimensions( + nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_OUTER | + nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION, + aX, aY, aCX, aCY); + } + return NS_ERROR_NULL_POINTER; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::Repaint(bool aForce) +{ + return NS_ERROR_NULL_POINTER; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::GetParentWidget(nsIWidget** aParentWidget) +{ + return NS_ERROR_NULL_POINTER; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::SetParentWidget(nsIWidget* aParentWidget) +{ + return NS_ERROR_NULL_POINTER; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::GetParentNativeWindow(nativeWindow* aParentNativeWindow) +{ + nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin(); + if (ownerWin) { + return ownerWin->GetSiteWindow(aParentNativeWindow); + } + return NS_ERROR_NULL_POINTER; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::SetParentNativeWindow(nativeWindow aParentNativeWindow) +{ + return NS_ERROR_NULL_POINTER; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::GetNativeHandle(nsAString& aNativeHandle) +{ + // the nativeHandle should be accessed from nsIXULWindow + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::GetVisibility(bool* aVisibility) +{ + nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin(); + if (ownerWin) { + return ownerWin->GetVisibility(aVisibility); + } + return NS_ERROR_NULL_POINTER; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::SetVisibility(bool aVisibility) +{ + nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin(); + if (ownerWin) { + return ownerWin->SetVisibility(aVisibility); + } + return NS_ERROR_NULL_POINTER; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::GetEnabled(bool* aEnabled) +{ + NS_ENSURE_ARG_POINTER(aEnabled); + *aEnabled = true; + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::SetEnabled(bool aEnabled) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::GetMainWidget(nsIWidget** aMainWidget) +{ + return NS_ERROR_NULL_POINTER; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::SetFocus() +{ + nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin(); + if (ownerWin) { + return ownerWin->SetFocus(); + } + return NS_ERROR_NULL_POINTER; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::GetTitle(char16_t** aTitle) +{ + nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin(); + if (ownerWin) { + return ownerWin->GetTitle(aTitle); + } + return NS_ERROR_NULL_POINTER; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::SetTitle(const char16_t* aTitle) +{ + nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin(); + if (ownerWin) { + return ownerWin->SetTitle(aTitle); + } + return NS_ERROR_NULL_POINTER; +} + +//***************************************************************************** +// nsDocShellTreeOwner::nsIWebProgressListener +//***************************************************************************** + +NS_IMETHODIMP +nsDocShellTreeOwner::OnProgressChange(nsIWebProgress* aProgress, + nsIRequest* aRequest, + int32_t aCurSelfProgress, + int32_t aMaxSelfProgress, + int32_t aCurTotalProgress, + int32_t aMaxTotalProgress) +{ + // In the absence of DOM document creation event, this method is the + // most convenient place to install the mouse listener on the + // DOM document. + return AddChromeListeners(); +} + +NS_IMETHODIMP +nsDocShellTreeOwner::OnStateChange(nsIWebProgress* aProgress, + nsIRequest* aRequest, + uint32_t aProgressStateFlags, + nsresult aStatus) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::OnLocationChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + nsIURI* aURI, + uint32_t aFlags) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::OnStatusChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + nsresult aStatus, + const char16_t* aMessage) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::OnSecurityChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + uint32_t aState) +{ + return NS_OK; +} + +//***************************************************************************** +// nsDocShellTreeOwner: Accessors +//***************************************************************************** + +void +nsDocShellTreeOwner::WebBrowser(nsWebBrowser* aWebBrowser) +{ + if (!aWebBrowser) { + RemoveChromeListeners(); + } + if (aWebBrowser != mWebBrowser) { + mPrompter = nullptr; + mAuthPrompter = nullptr; + } + + mWebBrowser = aWebBrowser; + + if (mContentTreeOwner) { + mContentTreeOwner->WebBrowser(aWebBrowser); + if (!aWebBrowser) { + mContentTreeOwner = nullptr; + } + } +} + +nsWebBrowser* +nsDocShellTreeOwner::WebBrowser() +{ + return mWebBrowser; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner) +{ + if (aTreeOwner) { + nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome(do_GetInterface(aTreeOwner)); + NS_ENSURE_TRUE(webBrowserChrome, NS_ERROR_INVALID_ARG); + NS_ENSURE_SUCCESS(SetWebBrowserChrome(webBrowserChrome), + NS_ERROR_INVALID_ARG); + mTreeOwner = aTreeOwner; + } else { + mTreeOwner = nullptr; + nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome(); + if (!webBrowserChrome) { + NS_ENSURE_SUCCESS(SetWebBrowserChrome(nullptr), NS_ERROR_FAILURE); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::SetWebBrowserChrome(nsIWebBrowserChrome* aWebBrowserChrome) +{ + if (!aWebBrowserChrome) { + mWebBrowserChrome = nullptr; + mOwnerWin = nullptr; + mOwnerRequestor = nullptr; + mWebBrowserChromeWeak = nullptr; + } else { + nsCOMPtr<nsISupportsWeakReference> supportsweak = + do_QueryInterface(aWebBrowserChrome); + if (supportsweak) { + supportsweak->GetWeakReference(getter_AddRefs(mWebBrowserChromeWeak)); + } else { + nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin( + do_QueryInterface(aWebBrowserChrome)); + nsCOMPtr<nsIInterfaceRequestor> requestor( + do_QueryInterface(aWebBrowserChrome)); + + // it's ok for ownerWin or requestor to be null. + mWebBrowserChrome = aWebBrowserChrome; + mOwnerWin = ownerWin; + mOwnerRequestor = requestor; + } + } + + if (mContentTreeOwner) { + mContentTreeOwner->SetWebBrowserChrome(aWebBrowserChrome); + } + + return NS_OK; +} + +// Hook up things to the chrome like context menus and tooltips, if the chrome +// has implemented the right interfaces. +NS_IMETHODIMP +nsDocShellTreeOwner::AddChromeListeners() +{ + nsresult rv = NS_OK; + + nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome(); + if (!webBrowserChrome) { + return NS_ERROR_FAILURE; + } + + // install tooltips + if (!mChromeTooltipListener) { + nsCOMPtr<nsITooltipListener> tooltipListener( + do_QueryInterface(webBrowserChrome)); + if (tooltipListener) { + mChromeTooltipListener = new ChromeTooltipListener(mWebBrowser, + webBrowserChrome); + rv = mChromeTooltipListener->AddChromeListeners(); + } + } + + // install context menus + if (!mChromeContextMenuListener) { + nsCOMPtr<nsIContextMenuListener2> contextListener2( + do_QueryInterface(webBrowserChrome)); + nsCOMPtr<nsIContextMenuListener> contextListener( + do_QueryInterface(webBrowserChrome)); + if (contextListener2 || contextListener) { + mChromeContextMenuListener = + new ChromeContextMenuListener(mWebBrowser, webBrowserChrome); + rv = mChromeContextMenuListener->AddChromeListeners(); + } + } + + // register dragover and drop event listeners with the listener manager + nsCOMPtr<EventTarget> target; + GetDOMEventTarget(mWebBrowser, getter_AddRefs(target)); + + EventListenerManager* elmP = target->GetOrCreateListenerManager(); + if (elmP) { + elmP->AddEventListenerByType(this, NS_LITERAL_STRING("dragover"), + TrustedEventsAtSystemGroupBubble()); + elmP->AddEventListenerByType(this, NS_LITERAL_STRING("drop"), + TrustedEventsAtSystemGroupBubble()); + } + + return rv; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::RemoveChromeListeners() +{ + if (mChromeTooltipListener) { + mChromeTooltipListener->RemoveChromeListeners(); + mChromeTooltipListener = nullptr; + } + if (mChromeContextMenuListener) { + mChromeContextMenuListener->RemoveChromeListeners(); + mChromeContextMenuListener = nullptr; + } + + nsCOMPtr<EventTarget> piTarget; + GetDOMEventTarget(mWebBrowser, getter_AddRefs(piTarget)); + if (!piTarget) { + return NS_OK; + } + + EventListenerManager* elmP = piTarget->GetOrCreateListenerManager(); + if (elmP) { + elmP->RemoveEventListenerByType(this, NS_LITERAL_STRING("dragover"), + TrustedEventsAtSystemGroupBubble()); + elmP->RemoveEventListenerByType(this, NS_LITERAL_STRING("drop"), + TrustedEventsAtSystemGroupBubble()); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::HandleEvent(nsIDOMEvent* aEvent) +{ + nsCOMPtr<nsIDOMDragEvent> dragEvent = do_QueryInterface(aEvent); + NS_ENSURE_TRUE(dragEvent, NS_ERROR_INVALID_ARG); + + bool defaultPrevented; + aEvent->GetDefaultPrevented(&defaultPrevented); + if (defaultPrevented) { + return NS_OK; + } + + nsCOMPtr<nsIDroppedLinkHandler> handler = + do_GetService("@mozilla.org/content/dropped-link-handler;1"); + if (handler) { + nsAutoString eventType; + aEvent->GetType(eventType); + if (eventType.EqualsLiteral("dragover")) { + bool canDropLink = false; + handler->CanDropLink(dragEvent, false, &canDropLink); + if (canDropLink) { + aEvent->PreventDefault(); + } + } else if (eventType.EqualsLiteral("drop")) { + nsIWebNavigation* webnav = static_cast<nsIWebNavigation*>(mWebBrowser); + + uint32_t linksCount; + nsIDroppedLinkItem** links; + if (webnav && + NS_SUCCEEDED(handler->DropLinks(dragEvent, true, &linksCount, &links))) { + if (linksCount >= 1) { + nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome(); + if (webBrowserChrome) { + nsCOMPtr<nsITabChild> tabChild = do_QueryInterface(webBrowserChrome); + if (tabChild) { + nsresult rv = tabChild->RemoteDropLinks(linksCount, links); + for (uint32_t i = 0; i < linksCount; i++) { + NS_RELEASE(links[i]); + } + free(links); + return rv; + } + } + nsAutoString url; + if (NS_SUCCEEDED(links[0]->GetUrl(url))) { + if (!url.IsEmpty()) { + webnav->LoadURI(url.get(), 0, nullptr, nullptr, nullptr); + } + } + + for (uint32_t i = 0; i < linksCount; i++) { + NS_RELEASE(links[i]); + } + free(links); + } + } else { + aEvent->StopPropagation(); + aEvent->PreventDefault(); + } + } + } + + return NS_OK; +} + +already_AddRefed<nsIWebBrowserChrome> +nsDocShellTreeOwner::GetWebBrowserChrome() +{ + nsCOMPtr<nsIWebBrowserChrome> chrome; + if (mWebBrowserChromeWeak) { + chrome = do_QueryReferent(mWebBrowserChromeWeak); + } else if (mWebBrowserChrome) { + chrome = mWebBrowserChrome; + } + return chrome.forget(); +} + +already_AddRefed<nsIEmbeddingSiteWindow> +nsDocShellTreeOwner::GetOwnerWin() +{ + nsCOMPtr<nsIEmbeddingSiteWindow> win; + if (mWebBrowserChromeWeak) { + win = do_QueryReferent(mWebBrowserChromeWeak); + } else if (mOwnerWin) { + win = mOwnerWin; + } + return win.forget(); +} + +already_AddRefed<nsIInterfaceRequestor> +nsDocShellTreeOwner::GetOwnerRequestor() +{ + nsCOMPtr<nsIInterfaceRequestor> req; + if (mWebBrowserChromeWeak) { + req = do_QueryReferent(mWebBrowserChromeWeak); + } else if (mOwnerRequestor) { + req = mOwnerRequestor; + } + return req.forget(); +} + +NS_IMPL_ISUPPORTS(ChromeTooltipListener, nsIDOMEventListener) + +ChromeTooltipListener::ChromeTooltipListener(nsWebBrowser* aInBrowser, + nsIWebBrowserChrome* aInChrome) + : mWebBrowser(aInBrowser) + , mWebBrowserChrome(aInChrome) + , mTooltipListenerInstalled(false) + , mMouseClientX(0) + , mMouseClientY(0) + , mMouseScreenX(0) + , mMouseScreenY(0) + , mShowingTooltip(false) + , mTooltipShownOnce(false) +{ + mTooltipTextProvider = do_GetService(NS_TOOLTIPTEXTPROVIDER_CONTRACTID); + if (!mTooltipTextProvider) { + mTooltipTextProvider = do_GetService(NS_DEFAULTTOOLTIPTEXTPROVIDER_CONTRACTID); + } +} + +ChromeTooltipListener::~ChromeTooltipListener() +{ +} + +// Hook up things to the chrome like context menus and tooltips, if the chrome +// has implemented the right interfaces. +NS_IMETHODIMP +ChromeTooltipListener::AddChromeListeners() +{ + if (!mEventTarget) { + GetDOMEventTarget(mWebBrowser, getter_AddRefs(mEventTarget)); + } + + // Register the appropriate events for tooltips, but only if + // the embedding chrome cares. + nsresult rv = NS_OK; + nsCOMPtr<nsITooltipListener> tooltipListener( + do_QueryInterface(mWebBrowserChrome)); + if (tooltipListener && !mTooltipListenerInstalled) { + rv = AddTooltipListener(); + if (NS_FAILED(rv)) { + return rv; + } + } + + return rv; +} + +// Subscribe to the events that will allow us to track tooltips. We need "mouse" +// for mouseExit, "mouse motion" for mouseMove, and "key" for keyDown. As we +// add the listeners, keep track of how many succeed so we can clean up +// correctly in Release(). +NS_IMETHODIMP +ChromeTooltipListener::AddTooltipListener() +{ + if (mEventTarget) { + nsresult rv = NS_OK; + rv = mEventTarget->AddSystemEventListener(NS_LITERAL_STRING("keydown"), + this, false, false); + NS_ENSURE_SUCCESS(rv, rv); + rv = mEventTarget->AddSystemEventListener(NS_LITERAL_STRING("mousedown"), + this, false, false); + NS_ENSURE_SUCCESS(rv, rv); + rv = mEventTarget->AddSystemEventListener(NS_LITERAL_STRING("mouseout"), + this, false, false); + NS_ENSURE_SUCCESS(rv, rv); + rv = mEventTarget->AddSystemEventListener(NS_LITERAL_STRING("mousemove"), + this, false, false); + NS_ENSURE_SUCCESS(rv, rv); + + mTooltipListenerInstalled = true; + } + + return NS_OK; +} + +// Unsubscribe from the various things we've hooked up to the window root. +NS_IMETHODIMP +ChromeTooltipListener::RemoveChromeListeners() +{ + HideTooltip(); + + if (mTooltipListenerInstalled) { + RemoveTooltipListener(); + } + + mEventTarget = nullptr; + + // it really doesn't matter if these fail... + return NS_OK; +} + +// Unsubscribe from all the various tooltip events that we were listening to. +NS_IMETHODIMP +ChromeTooltipListener::RemoveTooltipListener() +{ + if (mEventTarget) { + nsresult rv = NS_OK; + rv = mEventTarget->RemoveSystemEventListener(NS_LITERAL_STRING("keydown"), + this, false); + NS_ENSURE_SUCCESS(rv, rv); + rv = mEventTarget->RemoveSystemEventListener(NS_LITERAL_STRING("mousedown"), + this, false); + NS_ENSURE_SUCCESS(rv, rv); + rv = mEventTarget->RemoveSystemEventListener(NS_LITERAL_STRING("mouseout"), + this, false); + NS_ENSURE_SUCCESS(rv, rv); + rv = mEventTarget->RemoveSystemEventListener(NS_LITERAL_STRING("mousemove"), + this, false); + NS_ENSURE_SUCCESS(rv, rv); + + mTooltipListenerInstalled = false; + } + + return NS_OK; +} + +NS_IMETHODIMP +ChromeTooltipListener::HandleEvent(nsIDOMEvent* aEvent) +{ + nsAutoString eventType; + aEvent->GetType(eventType); + + if (eventType.EqualsLiteral("keydown") || + eventType.EqualsLiteral("mousedown")) { + return HideTooltip(); + } else if (eventType.EqualsLiteral("mouseout")) { + // Reset flag so that tooltip will display on the next MouseMove + mTooltipShownOnce = false; + return HideTooltip(); + } else if (eventType.EqualsLiteral("mousemove")) { + return MouseMove(aEvent); + } + + NS_ERROR("Unexpected event type"); + return NS_OK; +} + +// If we're a tooltip, fire off a timer to see if a tooltip should be shown. If +// the timer fires, we cache the node in |mPossibleTooltipNode|. +nsresult +ChromeTooltipListener::MouseMove(nsIDOMEvent* aMouseEvent) +{ + nsCOMPtr<nsIDOMMouseEvent> mouseEvent(do_QueryInterface(aMouseEvent)); + if (!mouseEvent) { + return NS_OK; + } + + // stash the coordinates of the event so that we can still get back to it from + // within the timer callback. On win32, we'll get a MouseMove event even when + // a popup goes away -- even when the mouse doesn't change position! To get + // around this, we make sure the mouse has really moved before proceeding. + int32_t newMouseX, newMouseY; + mouseEvent->GetClientX(&newMouseX); + mouseEvent->GetClientY(&newMouseY); + if (mMouseClientX == newMouseX && mMouseClientY == newMouseY) { + return NS_OK; + } + + // Filter out minor mouse movements. + if (mShowingTooltip && + (abs(mMouseClientX - newMouseX) <= kTooltipMouseMoveTolerance) && + (abs(mMouseClientY - newMouseY) <= kTooltipMouseMoveTolerance)) { + return NS_OK; + } + + mMouseClientX = newMouseX; + mMouseClientY = newMouseY; + mouseEvent->GetScreenX(&mMouseScreenX); + mouseEvent->GetScreenY(&mMouseScreenY); + + if (mTooltipTimer) { + mTooltipTimer->Cancel(); + } + + if (!mShowingTooltip && !mTooltipShownOnce) { + mTooltipTimer = do_CreateInstance("@mozilla.org/timer;1"); + if (mTooltipTimer) { + nsCOMPtr<EventTarget> eventTarget = + aMouseEvent->InternalDOMEvent()->GetTarget(); + if (eventTarget) { + mPossibleTooltipNode = do_QueryInterface(eventTarget); + } + if (mPossibleTooltipNode) { + nsresult rv = mTooltipTimer->InitWithFuncCallback( + sTooltipCallback, this, + LookAndFeel::GetInt(LookAndFeel::eIntID_TooltipDelay, 500), + nsITimer::TYPE_ONE_SHOT); + if (NS_FAILED(rv)) { + mPossibleTooltipNode = nullptr; + } + } + } else { + NS_WARNING("Could not create a timer for tooltip tracking"); + } + } else { + mTooltipShownOnce = true; + return HideTooltip(); + } + + return NS_OK; +} + +// Tell the registered chrome that they should show the tooltip. +NS_IMETHODIMP +ChromeTooltipListener::ShowTooltip(int32_t aInXCoords, int32_t aInYCoords, + const nsAString& aInTipText, + const nsAString& aTipDir) +{ + nsresult rv = NS_OK; + + // do the work to call the client + nsCOMPtr<nsITooltipListener> tooltipListener( + do_QueryInterface(mWebBrowserChrome)); + if (tooltipListener) { + rv = tooltipListener->OnShowTooltip(aInXCoords, aInYCoords, + PromiseFlatString(aInTipText).get(), + PromiseFlatString(aTipDir).get()); + if (NS_SUCCEEDED(rv)) { + mShowingTooltip = true; + } + } + + return rv; +} + +// Tell the registered chrome that they should rollup the tooltip +// NOTE: This routine is safe to call even if the popup is already closed. +NS_IMETHODIMP +ChromeTooltipListener::HideTooltip() +{ + nsresult rv = NS_OK; + + // shut down the relevant timers + if (mTooltipTimer) { + mTooltipTimer->Cancel(); + mTooltipTimer = nullptr; + // release tooltip target + mPossibleTooltipNode = nullptr; + } + + // if we're showing the tip, tell the chrome to hide it + if (mShowingTooltip) { + nsCOMPtr<nsITooltipListener> tooltipListener( + do_QueryInterface(mWebBrowserChrome)); + if (tooltipListener) { + rv = tooltipListener->OnHideTooltip(); + if (NS_SUCCEEDED(rv)) { + mShowingTooltip = false; + } + } + } + + return rv; +} + +// A timer callback, fired when the mouse has hovered inside of a frame for the +// appropriate amount of time. Getting to this point means that we should show +// the tooltip, but only after we determine there is an appropriate TITLE +// element. +// +// This relies on certain things being cached into the |aChromeTooltipListener| +// object passed to us by the timer: +// -- the x/y coordinates of the mouse (mMouseClientY, mMouseClientX) +// -- the dom node the user hovered over (mPossibleTooltipNode) +void +ChromeTooltipListener::sTooltipCallback(nsITimer* aTimer, + void* aChromeTooltipListener) +{ + auto self = static_cast<ChromeTooltipListener*>(aChromeTooltipListener); + if (self && self->mPossibleTooltipNode) { + // The actual coordinates we want to put the tooltip at are relative to the + // toplevel docshell of our mWebBrowser. We know what the screen + // coordinates of the mouse event were, which means we just need the screen + // coordinates of the docshell. Unfortunately, there is no good way to + // find those short of groveling for the presentation in that docshell and + // finding the screen coords of its toplevel widget... + nsCOMPtr<nsIDocShell> docShell = + do_GetInterface(static_cast<nsIWebBrowser*>(self->mWebBrowser)); + nsCOMPtr<nsIPresShell> shell; + if (docShell) { + shell = docShell->GetPresShell(); + } + + nsIWidget* widget = nullptr; + if (shell) { + nsViewManager* vm = shell->GetViewManager(); + if (vm) { + nsView* view = vm->GetRootView(); + if (view) { + nsPoint offset; + widget = view->GetNearestWidget(&offset); + } + } + } + + if (!widget) { + // release tooltip target if there is one, NO MATTER WHAT + self->mPossibleTooltipNode = nullptr; + return; + } + + // if there is text associated with the node, show the tip and fire + // off a timer to auto-hide it. + + nsXPIDLString tooltipText; + nsXPIDLString directionText; + if (self->mTooltipTextProvider) { + bool textFound = false; + + self->mTooltipTextProvider->GetNodeText( + self->mPossibleTooltipNode, getter_Copies(tooltipText), + getter_Copies(directionText), &textFound); + + if (textFound) { + nsString tipText(tooltipText); + nsString dirText(directionText); + LayoutDeviceIntPoint screenDot = widget->WidgetToScreenOffset(); + double scaleFactor = 1.0; + if (shell->GetPresContext()) { + nsDeviceContext* dc = shell->GetPresContext()->DeviceContext(); + scaleFactor = double(nsPresContext::AppUnitsPerCSSPixel()) / + dc->AppUnitsPerDevPixelAtUnitFullZoom(); + } + // ShowTooltip expects widget-relative position. + self->ShowTooltip(self->mMouseScreenX - screenDot.x / scaleFactor, + self->mMouseScreenY - screenDot.y / scaleFactor, + tipText, dirText); + } + } + + // release tooltip target if there is one, NO MATTER WHAT + self->mPossibleTooltipNode = nullptr; + } +} + +NS_IMPL_ISUPPORTS(ChromeContextMenuListener, nsIDOMEventListener) + +ChromeContextMenuListener::ChromeContextMenuListener( + nsWebBrowser* aInBrowser, + nsIWebBrowserChrome* aInChrome) + : mContextMenuListenerInstalled(false) + , mWebBrowser(aInBrowser) + , mWebBrowserChrome(aInChrome) +{ +} + +ChromeContextMenuListener::~ChromeContextMenuListener() +{ +} + +// Subscribe to the events that will allow us to track context menus. Bascially, +// this is just the context-menu DOM event. +NS_IMETHODIMP +ChromeContextMenuListener::AddContextMenuListener() +{ + if (mEventTarget) { + nsresult rv = mEventTarget->AddEventListener( + NS_LITERAL_STRING("contextmenu"), this, false, false); + NS_ENSURE_SUCCESS(rv, rv); + + mContextMenuListenerInstalled = true; + } + + return NS_OK; +} + +// Unsubscribe from all the various context menu events that we were listening +// to. +NS_IMETHODIMP +ChromeContextMenuListener::RemoveContextMenuListener() +{ + if (mEventTarget) { + nsresult rv = mEventTarget->RemoveEventListener( + NS_LITERAL_STRING("contextmenu"), this, false); + NS_ENSURE_SUCCESS(rv, rv); + + mContextMenuListenerInstalled = false; + } + + return NS_OK; +} + +// Hook up things to the chrome like context menus and tooltips, if the chrome +// has implemented the right interfaces. +NS_IMETHODIMP +ChromeContextMenuListener::AddChromeListeners() +{ + if (!mEventTarget) { + GetDOMEventTarget(mWebBrowser, getter_AddRefs(mEventTarget)); + } + + // Register the appropriate events for context menus, but only if + // the embedding chrome cares. + nsresult rv = NS_OK; + + nsCOMPtr<nsIContextMenuListener2> contextListener2( + do_QueryInterface(mWebBrowserChrome)); + nsCOMPtr<nsIContextMenuListener> contextListener( + do_QueryInterface(mWebBrowserChrome)); + if ((contextListener || contextListener2) && !mContextMenuListenerInstalled) { + rv = AddContextMenuListener(); + } + + return rv; +} + +// Unsubscribe from the various things we've hooked up to the window root. +NS_IMETHODIMP +ChromeContextMenuListener::RemoveChromeListeners() +{ + if (mContextMenuListenerInstalled) { + RemoveContextMenuListener(); + } + + mEventTarget = nullptr; + + // it really doesn't matter if these fail... + return NS_OK; +} + +// We're on call to show the context menu. Dig around in the DOM to find the +// type of object we're dealing with and notify the front end chrome. +NS_IMETHODIMP +ChromeContextMenuListener::HandleEvent(nsIDOMEvent* aMouseEvent) +{ + nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aMouseEvent); + NS_ENSURE_TRUE(mouseEvent, NS_ERROR_UNEXPECTED); + + bool isDefaultPrevented = false; + aMouseEvent->GetDefaultPrevented(&isDefaultPrevented); + if (isDefaultPrevented) { + return NS_OK; + } + + nsCOMPtr<EventTarget> targetNode = + aMouseEvent->InternalDOMEvent()->GetTarget(); + if (!targetNode) { + return NS_ERROR_NULL_POINTER; + } + + nsCOMPtr<nsIDOMNode> targetDOMnode; + nsCOMPtr<nsIDOMNode> node = do_QueryInterface(targetNode); + if (!node) { + return NS_OK; + } + + // Stop the context menu event going to other windows (bug 78396) + aMouseEvent->PreventDefault(); + + // If the listener is a nsIContextMenuListener2, create the info object + nsCOMPtr<nsIContextMenuListener2> menuListener2( + do_QueryInterface(mWebBrowserChrome)); + nsContextMenuInfo* menuInfoImpl = nullptr; + nsCOMPtr<nsIContextMenuInfo> menuInfo; + if (menuListener2) { + menuInfoImpl = new nsContextMenuInfo; + menuInfo = menuInfoImpl; + } + + uint32_t flags = nsIContextMenuListener::CONTEXT_NONE; + uint32_t flags2 = nsIContextMenuListener2::CONTEXT_NONE; + + // XXX test for selected text + + uint16_t nodeType; + nsresult res = node->GetNodeType(&nodeType); + NS_ENSURE_SUCCESS(res, res); + + // First, checks for nodes that never have children. + if (nodeType == nsIDOMNode::ELEMENT_NODE) { + nsCOMPtr<nsIImageLoadingContent> content(do_QueryInterface(node)); + if (content) { + nsCOMPtr<nsIURI> imgUri; + content->GetCurrentURI(getter_AddRefs(imgUri)); + if (imgUri) { + flags |= nsIContextMenuListener::CONTEXT_IMAGE; + flags2 |= nsIContextMenuListener2::CONTEXT_IMAGE; + targetDOMnode = node; + } + } + + nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(node)); + if (formControl) { + if (formControl->GetType() == NS_FORM_TEXTAREA) { + flags |= nsIContextMenuListener::CONTEXT_TEXT; + flags2 |= nsIContextMenuListener2::CONTEXT_TEXT; + targetDOMnode = node; + } else { + nsCOMPtr<nsIDOMHTMLInputElement> inputElement( + do_QueryInterface(formControl)); + if (inputElement) { + flags |= nsIContextMenuListener::CONTEXT_INPUT; + flags2 |= nsIContextMenuListener2::CONTEXT_INPUT; + + if (menuListener2) { + if (formControl->IsSingleLineTextControl(false)) { + flags2 |= nsIContextMenuListener2::CONTEXT_TEXT; + } + } + + targetDOMnode = node; + } + } + } + + // always consume events for plugins and Java who may throw their + // own context menus but not for image objects. Document objects + // will never be targets or ancestors of targets, so that's OK. + nsCOMPtr<nsIDOMHTMLObjectElement> objectElement; + if (!(flags & nsIContextMenuListener::CONTEXT_IMAGE)) { + objectElement = do_QueryInterface(node); + } + nsCOMPtr<nsIDOMHTMLEmbedElement> embedElement(do_QueryInterface(node)); + nsCOMPtr<nsIDOMHTMLAppletElement> appletElement(do_QueryInterface(node)); + + if (objectElement || embedElement || appletElement) { + return NS_OK; + } + } + + // Bubble out, looking for items of interest + do { + uint16_t nodeType; + res = node->GetNodeType(&nodeType); + NS_ENSURE_SUCCESS(res, res); + + if (nodeType == nsIDOMNode::ELEMENT_NODE) { + + // Test if the element has an associated link + nsCOMPtr<nsIDOMElement> element(do_QueryInterface(node)); + + bool hasAttr = false; + res = element->HasAttribute(NS_LITERAL_STRING("href"), &hasAttr); + + if (NS_SUCCEEDED(res) && hasAttr) { + flags |= nsIContextMenuListener::CONTEXT_LINK; + flags2 |= nsIContextMenuListener2::CONTEXT_LINK; + if (!targetDOMnode) { + targetDOMnode = node; + } + if (menuInfoImpl) { + menuInfoImpl->SetAssociatedLink(node); + } + break; // exit do-while + } + } + + // walk-up-the-tree + nsCOMPtr<nsIDOMNode> parentNode; + node->GetParentNode(getter_AddRefs(parentNode)); + node = parentNode; + } while (node); + + if (!flags && !flags2) { + // We found nothing of interest so far, check if we + // have at least an html document. + nsCOMPtr<nsIDOMDocument> document; + node = do_QueryInterface(targetNode); + node->GetOwnerDocument(getter_AddRefs(document)); + nsCOMPtr<nsIDOMHTMLDocument> htmlDocument(do_QueryInterface(document)); + if (htmlDocument) { + flags |= nsIContextMenuListener::CONTEXT_DOCUMENT; + flags2 |= nsIContextMenuListener2::CONTEXT_DOCUMENT; + targetDOMnode = node; + if (!(flags & nsIContextMenuListener::CONTEXT_IMAGE)) { + // check if this is a background image that the user was trying to click + // on and if the listener is ready for that (only + // nsIContextMenuListener2 and up) + if (menuInfoImpl && menuInfoImpl->HasBackgroundImage(targetDOMnode)) { + flags2 |= nsIContextMenuListener2::CONTEXT_BACKGROUND_IMAGE; + // For the embedder to get the correct background image + // targetDOMnode must point to the original node. + targetDOMnode = do_QueryInterface(targetNode); + } + } + } + } + + // we need to cache the event target into the focus controller's popupNode + // so we can get at it later from command code, etc.: + + // get the dom window + nsCOMPtr<mozIDOMWindowProxy> win; + res = mWebBrowser->GetContentDOMWindow(getter_AddRefs(win)); + NS_ENSURE_SUCCESS(res, res); + NS_ENSURE_TRUE(win, NS_ERROR_FAILURE); + + auto* window = nsPIDOMWindowOuter::From(win); + nsCOMPtr<nsPIWindowRoot> root = window->GetTopWindowRoot(); + NS_ENSURE_TRUE(root, NS_ERROR_FAILURE); + if (root) { + // set the window root's popup node to the event target + root->SetPopupNode(targetDOMnode); + } + + // Tell the listener all about the event + if (menuListener2) { + menuInfoImpl->SetMouseEvent(aMouseEvent); + menuInfoImpl->SetDOMNode(targetDOMnode); + menuListener2->OnShowContextMenu(flags2, menuInfo); + } else { + nsCOMPtr<nsIContextMenuListener> menuListener( + do_QueryInterface(mWebBrowserChrome)); + if (menuListener) { + menuListener->OnShowContextMenu(flags, aMouseEvent, targetDOMnode); + } + } + + return NS_OK; +} |