diff -r 000000000000 -r 6474c204b198 embedding/browser/webBrowser/nsDocShellTreeOwner.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/embedding/browser/webBrowser/nsDocShellTreeOwner.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,1825 @@ +/* -*- 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/. */ + +// Local Includes +#include "nsDocShellTreeOwner.h" +#include "nsWebBrowser.h" + +// Helper Classes +#include "nsStyleCoord.h" +#include "nsSize.h" +#include "nsHTMLReflowState.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 "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 "nsPIDOMWindow.h" +#include "nsPIWindowRoot.h" +#include "nsIDOMWindowCollection.h" +#include "nsIWindowWatcher.h" +#include "nsPIWindowWatcher.h" +#include "nsIPrompt.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() + +using namespace mozilla; +using namespace mozilla::dom; + +// +// GetEventReceiver +// +// 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* inBrowser, EventTarget** aTarget) +{ + NS_ENSURE_ARG_POINTER(inBrowser); + + nsCOMPtr domWindow; + inBrowser->GetContentDOMWindow(getter_AddRefs(domWindow)); + NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE); + + nsCOMPtr domWindowPrivate = do_QueryInterface(domWindow); + NS_ENSURE_TRUE(domWindowPrivate, NS_ERROR_FAILURE); + nsPIDOMWindow *rootWindow = domWindowPrivate->GetPrivateRoot(); + NS_ENSURE_TRUE(rootWindow, NS_ERROR_FAILURE); + nsCOMPtr target = + rootWindow->GetChromeEventHandler(); + NS_ENSURE_TRUE(target, NS_ERROR_FAILURE); + target.forget(aTarget); + + return NS_OK; +} + + +//***************************************************************************** +//*** nsDocShellTreeOwner: Object Management +//***************************************************************************** + +nsDocShellTreeOwner::nsDocShellTreeOwner() : + mWebBrowser(nullptr), + mTreeOwner(nullptr), + mPrimaryContentShell(nullptr), + mWebBrowserChrome(nullptr), + mOwnerWin(nullptr), + mOwnerRequestor(nullptr), + mChromeTooltipListener(nullptr), + mChromeContextMenuListener(nullptr) +{ +} + +nsDocShellTreeOwner::~nsDocShellTreeOwner() +{ + RemoveChromeListeners(); +} + +//***************************************************************************** +// nsDocShellTreeOwner::nsISupports +//***************************************************************************** + +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))) { + nsIPrompt *prompt; + EnsurePrompter(); + prompt = mPrompter; + if (prompt) { + NS_ADDREF(prompt); + *aSink = prompt; + return NS_OK; + } + return NS_NOINTERFACE; + } + + if (aIID.Equals(NS_GET_IID(nsIAuthPrompt))) { + nsIAuthPrompt *prompt; + EnsureAuthPrompter(); + prompt = mAuthPrompter; + if (prompt) { + NS_ADDREF(prompt); + *aSink = prompt; + return NS_OK; + } + return NS_NOINTERFACE; + } + + nsCOMPtr req = GetOwnerRequestor(); + if (req) + return req->GetInterface(aIID, aSink); + + return NS_NOINTERFACE; +} + +//***************************************************************************** +// nsDocShellTreeOwner::nsIDocShellTreeOwner +//***************************************************************************** + +NS_IMETHODIMP +nsDocShellTreeOwner::FindItemWithName(const char16_t* aName, + nsIDocShellTreeItem* aRequestor, + nsIDocShellTreeItem* aOriginalRequestor, + nsIDocShellTreeItem** aFoundItem) +{ + NS_ENSURE_ARG(aName); + NS_ENSURE_ARG_POINTER(aFoundItem); + *aFoundItem = nullptr; // if we don't find one, we return NS_OK and a null result + nsresult rv; + + nsAutoString name(aName); + + if (!mWebBrowser) + return NS_OK; // stymied + + /* special cases */ + if(name.IsEmpty()) + return NS_OK; + if(name.LowerCaseEqualsLiteral("_blank")) + return NS_OK; + // _main is an IE target which should be case-insensitive but isn't + // see bug 217886 for details + // XXXbz what if our browser isn't targetable? We need to handle that somehow. + if(name.LowerCaseEqualsLiteral("_content") || name.EqualsLiteral("_main")) { + *aFoundItem = mWebBrowser->mDocShell; + NS_IF_ADDREF(*aFoundItem); + return NS_OK; + } + + if (!SameCOMIdentity(aRequestor, mWebBrowser->mDocShell)) { + // This isn't a request coming up from our kid, so check with said kid + nsISupports* thisSupports = static_cast(this); + rv = mWebBrowser->mDocShell->FindItemWithName(aName, thisSupports, + aOriginalRequestor, aFoundItem); + if (NS_FAILED(rv) || *aFoundItem) { + return rv; + } + } + + // next, if we have a parent and it isn't the requestor, ask it + if(mTreeOwner) { + nsCOMPtr reqAsTreeOwner(do_QueryInterface(aRequestor)); + if (mTreeOwner != reqAsTreeOwner) + return mTreeOwner->FindItemWithName(aName, mWebBrowser->mDocShell, + aOriginalRequestor, aFoundItem); + return NS_OK; + } + + // finally, failing everything else, search all windows + return FindItemWithNameAcrossWindows(aName, aRequestor, aOriginalRequestor, + aFoundItem); +} + +nsresult +nsDocShellTreeOwner::FindItemWithNameAcrossWindows(const char16_t* aName, + nsIDocShellTreeItem* aRequestor, + nsIDocShellTreeItem* aOriginalRequestor, + nsIDocShellTreeItem** aFoundItem) +{ + // search for the item across the list of top-level windows + nsCOMPtr wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID)); + if (!wwatch) + return NS_OK; + + return wwatch->FindItemWithName(aName, aRequestor, aOriginalRequestor, + aFoundItem); +} + +void +nsDocShellTreeOwner::EnsurePrompter() +{ + if (mPrompter) + return; + + nsCOMPtr wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID)); + if (wwatch && mWebBrowser) { + nsCOMPtr domWindow; + mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow)); + if (domWindow) + wwatch->GetNewPrompter(domWindow, getter_AddRefs(mPrompter)); + } +} + +void +nsDocShellTreeOwner::EnsureAuthPrompter() +{ + if (mAuthPrompter) + return; + + nsCOMPtr wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID)); + if (wwatch && mWebBrowser) { + nsCOMPtr domWindow; + mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow)); + if (domWindow) + wwatch->GetNewAuthPrompter(domWindow, getter_AddRefs(mAuthPrompter)); + } +} + +void +nsDocShellTreeOwner::AddToWatcher() +{ + if (mWebBrowser) { + nsCOMPtr domWindow; + mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow)); + if (domWindow) { + nsCOMPtr wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID)); + if (wwatch) { + nsCOMPtr webBrowserChrome = GetWebBrowserChrome(); + if (webBrowserChrome) + wwatch->AddWindow(domWindow, webBrowserChrome); + } + } + } +} + +void +nsDocShellTreeOwner::RemoveFromWatcher() +{ + if (mWebBrowser) { + nsCOMPtr domWindow; + mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow)); + if (domWindow) { + nsCOMPtr wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID)); + if (wwatch) + wwatch->RemoveWindow(domWindow); + } + } +} + + +NS_IMETHODIMP +nsDocShellTreeOwner::ContentShellAdded(nsIDocShellTreeItem* aContentShell, + bool aPrimary, bool aTargetable, + const nsAString& aID) +{ + if(mTreeOwner) + return mTreeOwner->ContentShellAdded(aContentShell, aPrimary, + aTargetable, aID); + + if (aPrimary) + mPrimaryContentShell = aContentShell; + 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); + + *aShell = (mPrimaryContentShell ? mPrimaryContentShell : mWebBrowser->mDocShell); + NS_IF_ADDREF(*aShell); + + return NS_OK; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::GetContentWindow(JSContext* aCx, + JS::MutableHandle aVal) +{ + if (mTreeOwner) + return mTreeOwner->GetContentWindow(aCx, aVal); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::SizeShellTo(nsIDocShellTreeItem* aShellItem, + int32_t aCX, int32_t aCY) +{ + nsCOMPtr webBrowserChrome = GetWebBrowserChrome(); + + NS_ENSURE_STATE(mTreeOwner || webBrowserChrome); + + if(mTreeOwner) + return mTreeOwner->SizeShellTo(aShellItem, aCX, aCY); + + if(aShellItem == mWebBrowser->mDocShell) + return webBrowserChrome->SizeBrowserTo(aCX, aCY); + + nsCOMPtr webNav(do_QueryInterface(aShellItem)); + NS_ENSURE_TRUE(webNav, NS_ERROR_FAILURE); + + nsCOMPtr domDocument; + webNav->GetDocument(getter_AddRefs(domDocument)); + NS_ENSURE_TRUE(domDocument, NS_ERROR_FAILURE); + + nsCOMPtr 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. + */ + + nsRefPtr 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; +} + +//***************************************************************************** +// 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 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::SetPosition(int32_t aX, int32_t aY) +{ + nsCOMPtr 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 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 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 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, bool aRepaint) +{ + nsCOMPtr 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 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 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 ownerWin = GetOwnerWin(); + if (ownerWin) + { + return ownerWin->GetVisibility(aVisibility); + } + return NS_ERROR_NULL_POINTER; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::SetVisibility(bool aVisibility) +{ + nsCOMPtr 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 ownerWin = GetOwnerWin(); + if (ownerWin) + { + return ownerWin->SetFocus(); + } + return NS_ERROR_NULL_POINTER; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::GetTitle(char16_t** aTitle) +{ + nsCOMPtr ownerWin = GetOwnerWin(); + if (ownerWin) + { + return ownerWin->GetTitle(aTitle); + } + return NS_ERROR_NULL_POINTER; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::SetTitle(const char16_t* aTitle) +{ + nsCOMPtr 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 state) +{ + return NS_OK; +} + + +//***************************************************************************** +// nsDocShellTreeOwner: Helpers +//***************************************************************************** + +//***************************************************************************** +// nsDocShellTreeOwner: Accessors +//***************************************************************************** + +void +nsDocShellTreeOwner::WebBrowser(nsWebBrowser* aWebBrowser) +{ + if ( !aWebBrowser ) + RemoveChromeListeners(); + if (aWebBrowser != mWebBrowser) { + mPrompter = 0; + mAuthPrompter = 0; + } + + mWebBrowser = aWebBrowser; +} + +nsWebBrowser * +nsDocShellTreeOwner::WebBrowser() +{ + return mWebBrowser; +} + +NS_IMETHODIMP +nsDocShellTreeOwner::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner) +{ + if(aTreeOwner) { + nsCOMPtr 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 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 = 0; + } else { + nsCOMPtr supportsweak = + do_QueryInterface(aWebBrowserChrome); + if (supportsweak) { + supportsweak->GetWeakReference(getter_AddRefs(mWebBrowserChromeWeak)); + } else { + nsCOMPtr ownerWin(do_QueryInterface(aWebBrowserChrome)); + nsCOMPtr requestor(do_QueryInterface(aWebBrowserChrome)); + + // it's ok for ownerWin or requestor to be null. + mWebBrowserChrome = aWebBrowserChrome; + mOwnerWin = ownerWin; + mOwnerRequestor = requestor; + } + } + return NS_OK; +} + + +// +// AddChromeListeners +// +// 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 webBrowserChrome = GetWebBrowserChrome(); + if (!webBrowserChrome) + return NS_ERROR_FAILURE; + + // install tooltips + if ( !mChromeTooltipListener ) { + nsCOMPtr + tooltipListener(do_QueryInterface(webBrowserChrome)); + if ( tooltipListener ) { + mChromeTooltipListener = new ChromeTooltipListener(mWebBrowser, + webBrowserChrome); + if ( mChromeTooltipListener ) { + NS_ADDREF(mChromeTooltipListener); + rv = mChromeTooltipListener->AddChromeListeners(); + } + else + rv = NS_ERROR_OUT_OF_MEMORY; + } + } + + // install context menus + if ( !mChromeContextMenuListener ) { + nsCOMPtr + contextListener2(do_QueryInterface(webBrowserChrome)); + nsCOMPtr + contextListener(do_QueryInterface(webBrowserChrome)); + if ( contextListener2 || contextListener ) { + mChromeContextMenuListener = + new ChromeContextMenuListener(mWebBrowser, webBrowserChrome); + if ( mChromeContextMenuListener ) { + NS_ADDREF(mChromeContextMenuListener); + rv = mChromeContextMenuListener->AddChromeListeners(); + } + else + rv = NS_ERROR_OUT_OF_MEMORY; + } + } + + // register dragover and drop event listeners with the listener manager + nsCOMPtr 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; + +} // AddChromeListeners + + +NS_IMETHODIMP +nsDocShellTreeOwner::RemoveChromeListeners() +{ + if ( mChromeTooltipListener ) { + mChromeTooltipListener->RemoveChromeListeners(); + NS_RELEASE(mChromeTooltipListener); + } + if ( mChromeContextMenuListener ) { + mChromeContextMenuListener->RemoveChromeListeners(); + NS_RELEASE(mChromeContextMenuListener); + } + + nsCOMPtr 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 dragEvent = do_QueryInterface(aEvent); + NS_ENSURE_TRUE(dragEvent, NS_ERROR_INVALID_ARG); + + bool defaultPrevented; + aEvent->GetDefaultPrevented(&defaultPrevented); + if (defaultPrevented) { + return NS_OK; + } + + nsCOMPtr handler = do_GetService("@mozilla.org/content/dropped-link-handler;1"); + if (handler) { + nsAutoString eventType; + aEvent->GetType(eventType); + if (eventType.EqualsLiteral("dragover")) { + bool canDropLink; + handler->CanDropLink(dragEvent, false, &canDropLink); + if (canDropLink) + aEvent->PreventDefault(); + } + else if (eventType.EqualsLiteral("drop")) { + nsIWebNavigation* webnav = static_cast(mWebBrowser); + + nsAutoString link, name; + if (webnav && NS_SUCCEEDED(handler->DropLink(dragEvent, link, false, name))) { + if (!link.IsEmpty()) { + webnav->LoadURI(link.get(), 0, nullptr, nullptr, nullptr); + } + } + else { + aEvent->StopPropagation(); + aEvent->PreventDefault(); + } + } + } + + return NS_OK; +} + +already_AddRefed +nsDocShellTreeOwner::GetWebBrowserChrome() +{ + nsCOMPtr chrome; + if (mWebBrowserChromeWeak) { + chrome = do_QueryReferent(mWebBrowserChromeWeak); + } else if (mWebBrowserChrome) { + chrome = mWebBrowserChrome; + } + return chrome.forget(); +} + +already_AddRefed +nsDocShellTreeOwner::GetOwnerWin() +{ + nsCOMPtr win; + if (mWebBrowserChromeWeak) { + win = do_QueryReferent(mWebBrowserChromeWeak); + } else if (mOwnerWin) { + win = mOwnerWin; + } + return win.forget(); +} + +already_AddRefed +nsDocShellTreeOwner::GetOwnerRequestor() +{ + nsCOMPtr req; + if (mWebBrowserChromeWeak) { + req = do_QueryReferent(mWebBrowserChromeWeak); + } else if (mOwnerRequestor) { + req = mOwnerRequestor; + } + return req.forget(); +} + + +/////////////////////////////////////////////////////////////////////////////// +// DefaultTooltipTextProvider + +class DefaultTooltipTextProvider MOZ_FINAL : public nsITooltipTextProvider +{ +public: + DefaultTooltipTextProvider(); + + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSITOOLTIPTEXTPROVIDER + +protected: + nsCOMPtr mTag_dialog; + nsCOMPtr mTag_dialogheader; + nsCOMPtr mTag_window; +}; + +NS_IMPL_ISUPPORTS(DefaultTooltipTextProvider, nsITooltipTextProvider) + +DefaultTooltipTextProvider::DefaultTooltipTextProvider() +{ + // There are certain element types which we don't want to use + // as tool tip text. + mTag_dialog = do_GetAtom("dialog"); + mTag_dialogheader = do_GetAtom("dialogheader"); + mTag_window = do_GetAtom("window"); +} + +// +// UseSVGTitle +// +// A helper routine that determines whether we're still interested +// in SVG titles. We need to stop at the SVG root element that +// has a document node parent +// +static bool +UseSVGTitle(nsIDOMElement *currElement) +{ + nsCOMPtr element(do_QueryInterface(currElement)); + if (!element || !element->IsSVG() || !element->GetParentNode()) + return false; + + return element->GetParentNode()->NodeType() != nsIDOMNode::DOCUMENT_NODE; +} + +/* void getNodeText (in nsIDOMNode aNode, out wstring aText); */ +NS_IMETHODIMP +DefaultTooltipTextProvider::GetNodeText(nsIDOMNode *aNode, char16_t **aText, + bool *_retval) +{ + NS_ENSURE_ARG_POINTER(aNode); + NS_ENSURE_ARG_POINTER(aText); + + nsString outText; + + nsCOMPtr node = do_QueryInterface(aNode); + + bool lookingForSVGTitle = true; + bool found = false; + nsCOMPtr current ( aNode ); + + // If the element implement the constraint validation API and has no title, + // show the validation message, if any. + nsCOMPtr cvElement = do_QueryInterface(current); + if (cvElement) { + nsCOMPtr content = do_QueryInterface(cvElement); + nsCOMPtr titleAtom = do_GetAtom("title"); + + nsCOMPtr formControl = do_QueryInterface(content); + bool formHasNoValidate = false; + mozilla::dom::Element* form = formControl->GetFormElement(); + if (form) { + nsCOMPtr noValidateAtom = do_GetAtom("novalidate"); + formHasNoValidate = form->HasAttr(kNameSpaceID_None, noValidateAtom); + } + + if (!content->HasAttr(kNameSpaceID_None, titleAtom) && + !formHasNoValidate) { + cvElement->GetValidationMessage(outText); + found = !outText.IsEmpty(); + } + } + + while ( !found && current ) { + nsCOMPtr currElement ( do_QueryInterface(current) ); + if ( currElement ) { + nsCOMPtr content(do_QueryInterface(currElement)); + if (content) { + nsIAtom *tagAtom = content->Tag(); + if (tagAtom != mTag_dialog && + tagAtom != mTag_dialogheader && + tagAtom != mTag_window) { + // first try the normal title attribute... + currElement->GetAttribute(NS_LITERAL_STRING("title"), outText); + if ( outText.Length() ) + found = true; + else { + // ...ok, that didn't work, try it in the XLink namespace + NS_NAMED_LITERAL_STRING(xlinkNS, "http://www.w3.org/1999/xlink"); + nsCOMPtr linkContent(do_QueryInterface(currElement)); + if (linkContent) { + nsCOMPtr uri(linkContent->GetURIExternal()); + if (uri) { + currElement->GetAttributeNS(NS_LITERAL_STRING("http://www.w3.org/1999/xlink"), NS_LITERAL_STRING("title"), outText); + if ( outText.Length() ) + found = true; + } + } + else { + if (lookingForSVGTitle) { + lookingForSVGTitle = UseSVGTitle(currElement); + } + if (lookingForSVGTitle) { + nsINodeList* childNodes = node->ChildNodes(); + uint32_t childNodeCount = childNodes->Length(); + for (uint32_t i = 0; i < childNodeCount; i++) { + nsIContent* child = childNodes->Item(i); + if (child->IsSVG(nsGkAtoms::title)) { + static_cast(child)->GetTextContent(outText); + if ( outText.Length() ) + found = true; + break; + } + } + } + } + } + } + } + } + + // not found here, walk up to the parent and keep trying + if ( !found ) { + nsCOMPtr temp ( current ); + temp->GetParentNode(getter_AddRefs(current)); + } + } // while not found + + *_retval = found; + *aText = (found) ? ToNewUnicode(outText) : nullptr; + + return NS_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +NS_IMPL_ISUPPORTS(ChromeTooltipListener, nsIDOMEventListener) + +// +// ChromeTooltipListener ctor +// + +ChromeTooltipListener::ChromeTooltipListener(nsWebBrowser* inBrowser, + nsIWebBrowserChrome* inChrome) + : mWebBrowser(inBrowser), mWebBrowserChrome(inChrome), + mTooltipListenerInstalled(false), + mMouseClientX(0), mMouseClientY(0), + mShowingTooltip(false) +{ + mTooltipTextProvider = do_GetService(NS_TOOLTIPTEXTPROVIDER_CONTRACTID); + if (!mTooltipTextProvider) { + nsISupports *pProvider = (nsISupports *) new DefaultTooltipTextProvider; + mTooltipTextProvider = do_QueryInterface(pProvider); + } +} // ctor + + +// +// ChromeTooltipListener dtor +// +ChromeTooltipListener::~ChromeTooltipListener() +{ + +} // dtor + + +// +// AddChromeListeners +// +// 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 tooltipListener ( do_QueryInterface(mWebBrowserChrome) ); + if ( tooltipListener && !mTooltipListenerInstalled ) { + rv = AddTooltipListener(); + if ( NS_FAILED(rv) ) + return rv; + } + + return rv; + +} // AddChromeListeners + + +// +// AddTooltipListener +// +// 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 = mEventTarget->AddEventListener(NS_LITERAL_STRING("keydown"), + this, false, false); + NS_ENSURE_SUCCESS(rv, rv); + rv = mEventTarget->AddEventListener(NS_LITERAL_STRING("mousedown"), this, + false, false); + NS_ENSURE_SUCCESS(rv, rv); + rv = mEventTarget->AddEventListener(NS_LITERAL_STRING("mouseout"), this, + false, false); + NS_ENSURE_SUCCESS(rv, rv); + rv = mEventTarget->AddEventListener(NS_LITERAL_STRING("mousemove"), this, + false, false); + NS_ENSURE_SUCCESS(rv, rv); + + mTooltipListenerInstalled = true; + } + + return NS_OK; +} + + +// +// RemoveChromeListeners +// +// 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; + +} // RemoveChromeTooltipListeners + + + +// +// RemoveTooltipListener +// +// Unsubscribe from all the various tooltip events that we were listening to +// +NS_IMETHODIMP +ChromeTooltipListener::RemoveTooltipListener() +{ + if (mEventTarget) { + nsresult rv = + mEventTarget->RemoveEventListener(NS_LITERAL_STRING("keydown"), this, + false); + NS_ENSURE_SUCCESS(rv, rv); + rv = mEventTarget->RemoveEventListener(NS_LITERAL_STRING("mousedown"), + this, false); + NS_ENSURE_SUCCESS(rv, rv); + rv = mEventTarget->RemoveEventListener(NS_LITERAL_STRING("mouseout"), this, + false); + NS_ENSURE_SUCCESS(rv, rv); + rv = mEventTarget->RemoveEventListener(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") || + eventType.EqualsLiteral("mouseout")) + return HideTooltip(); + if (eventType.EqualsLiteral("mousemove")) + return MouseMove(aEvent); + + NS_ERROR("Unexpected event type"); + return NS_OK; +} + +// +// MouseMove +// +// 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 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; + mMouseClientX = newMouseX; mMouseClientY = newMouseY; + mouseEvent->GetScreenX(&mMouseScreenX); + mouseEvent->GetScreenY(&mMouseScreenY); + + // We want to close the tip if it is being displayed and the mouse moves. Recall + // that |mShowingTooltip| is set when the popup is showing. Furthermore, as the mouse + // moves, we want to make sure we reset the timer to show it, so that the delay + // is from when the mouse stops moving, not when it enters the element. + if ( mShowingTooltip ) + return HideTooltip(); + if ( mTooltipTimer ) + mTooltipTimer->Cancel(); + + mTooltipTimer = do_CreateInstance("@mozilla.org/timer;1"); + if ( mTooltipTimer ) { + nsCOMPtr 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" ); + + return NS_OK; + +} // MouseMove + + +// +// ShowTooltip +// +// Tell the registered chrome that they should show the tooltip +// +NS_IMETHODIMP +ChromeTooltipListener::ShowTooltip(int32_t inXCoords, int32_t inYCoords, + const nsAString & inTipText) +{ + nsresult rv = NS_OK; + + // do the work to call the client + nsCOMPtr tooltipListener ( do_QueryInterface(mWebBrowserChrome) ); + if ( tooltipListener ) { + rv = tooltipListener->OnShowTooltip ( inXCoords, inYCoords, PromiseFlatString(inTipText).get() ); + if ( NS_SUCCEEDED(rv) ) + mShowingTooltip = true; + } + + return rv; + +} // ShowTooltip + + +// +// HideTooltip +// +// 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 ( mAutoHideTimer ) { + mAutoHideTimer->Cancel(); + mAutoHideTimer = nullptr; + } + + // if we're showing the tip, tell the chrome to hide it + if ( mShowingTooltip ) { + nsCOMPtr tooltipListener ( do_QueryInterface(mWebBrowserChrome) ); + if ( tooltipListener ) { + rv = tooltipListener->OnHideTooltip ( ); + if ( NS_SUCCEEDED(rv) ) + mShowingTooltip = false; + } + } + + return rv; + +} // HideTooltip + + +// +// sTooltipCallback +// +// 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) +{ + ChromeTooltipListener* self = static_cast + (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 docShell = + do_GetInterface(static_cast(self->mWebBrowser)); + nsCOMPtr 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; + if (self->mTooltipTextProvider) { + bool textFound = false; + + self->mTooltipTextProvider->GetNodeText( + self->mPossibleTooltipNode, getter_Copies(tooltipText), &textFound); + + if (textFound) { + nsString tipText(tooltipText); + self->CreateAutoHideTimer(); + nsIntPoint screenDot = widget->WidgetToScreenOffset(); + self->ShowTooltip (self->mMouseScreenX - screenDot.x, + self->mMouseScreenY - screenDot.y, + tipText); + } + } + + // release tooltip target if there is one, NO MATTER WHAT + self->mPossibleTooltipNode = nullptr; + } // if "self" data valid + +} // sTooltipCallback + + +// +// CreateAutoHideTimer +// +// Create a new timer to see if we should auto-hide. It's ok if this fails. +// +void +ChromeTooltipListener::CreateAutoHideTimer() +{ + // just to be anal (er, safe) + if ( mAutoHideTimer ) { + mAutoHideTimer->Cancel(); + mAutoHideTimer = nullptr; + } + + mAutoHideTimer = do_CreateInstance("@mozilla.org/timer;1"); + if ( mAutoHideTimer ) + mAutoHideTimer->InitWithFuncCallback(sAutoHideCallback, this, kTooltipAutoHideTime, + nsITimer::TYPE_ONE_SHOT); + +} // CreateAutoHideTimer + + +// +// sAutoHideCallback +// +// This fires after a tooltip has been open for a certain length of time. Just tell +// the listener to close the popup. We don't have to worry, because HideTooltip() can +// be called multiple times, even if the tip has already been closed. +// +void +ChromeTooltipListener::sAutoHideCallback(nsITimer *aTimer, void* aListener) +{ + ChromeTooltipListener* self = static_cast(aListener); + if ( self ) + self->HideTooltip(); + + // NOTE: |aTimer| and |self->mAutoHideTimer| are invalid after calling ClosePopup(); + +} // sAutoHideCallback + + +NS_IMPL_ISUPPORTS(ChromeContextMenuListener, nsIDOMEventListener) + + +// +// ChromeTooltipListener ctor +// +ChromeContextMenuListener::ChromeContextMenuListener(nsWebBrowser* inBrowser, nsIWebBrowserChrome* inChrome ) + : mContextMenuListenerInstalled(false), + mWebBrowser(inBrowser), + mWebBrowserChrome(inChrome) +{ +} // ctor + + +// +// ChromeTooltipListener dtor +// +ChromeContextMenuListener::~ChromeContextMenuListener() +{ +} // dtor + + +// +// AddContextMenuListener +// +// 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; +} + + +// +// RemoveContextMenuListener +// +// 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; +} + + +// +// AddChromeListeners +// +// 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 contextListener2 ( do_QueryInterface(mWebBrowserChrome) ); + nsCOMPtr contextListener ( do_QueryInterface(mWebBrowserChrome) ); + if ( (contextListener || contextListener2) && !mContextMenuListenerInstalled ) + rv = AddContextMenuListener(); + + return rv; + +} // AddChromeListeners + + +// +// RemoveChromeListeners +// +// 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; + +} // RemoveChromeTooltipListeners + + + +// +// ContextMenu +// +// 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 mouseEvent = do_QueryInterface(aMouseEvent); + NS_ENSURE_TRUE(mouseEvent, NS_ERROR_UNEXPECTED); + + bool isDefaultPrevented = false; + aMouseEvent->GetDefaultPrevented(&isDefaultPrevented); + if (isDefaultPrevented) { + return NS_OK; + } + + nsCOMPtr targetNode = aMouseEvent->InternalDOMEvent()->GetTarget(); + if (!targetNode) + return NS_ERROR_NULL_POINTER; + + nsCOMPtr targetDOMnode; + nsCOMPtr 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 menuListener2(do_QueryInterface(mWebBrowserChrome)); + nsContextMenuInfo *menuInfoImpl = nullptr; + nsCOMPtr 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 content(do_QueryInterface(node)); + if (content) { + nsCOMPtr imgUri; + content->GetCurrentURI(getter_AddRefs(imgUri)); + if (imgUri) { + flags |= nsIContextMenuListener::CONTEXT_IMAGE; + flags2 |= nsIContextMenuListener2::CONTEXT_IMAGE; + targetDOMnode = node; + } + } + + nsCOMPtr formControl(do_QueryInterface(node)); + if (formControl) { + if (formControl->GetType() == NS_FORM_TEXTAREA) { + flags |= nsIContextMenuListener::CONTEXT_TEXT; + flags2 |= nsIContextMenuListener2::CONTEXT_TEXT; + targetDOMnode = node; + } else { + nsCOMPtr 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 objectElement; + if (!(flags & nsIContextMenuListener::CONTEXT_IMAGE)) + objectElement = do_QueryInterface(node); + nsCOMPtr embedElement(do_QueryInterface(node)); + nsCOMPtr 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 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 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 document; + node = do_QueryInterface(targetNode); + node->GetOwnerDocument(getter_AddRefs(document)); + nsCOMPtr 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 win; + res = mWebBrowser->GetContentDOMWindow(getter_AddRefs(win)); + NS_ENSURE_SUCCESS(res, res); + NS_ENSURE_TRUE(win, NS_ERROR_FAILURE); + + nsCOMPtr window(do_QueryInterface(win)); + NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); + nsCOMPtr 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 menuListener(do_QueryInterface(mWebBrowserChrome)); + if ( menuListener ) + menuListener->OnShowContextMenu(flags, aMouseEvent, targetDOMnode); + } + + return NS_OK; + +} // MouseDown