Bumping manifests a=b2g-bump
[gecko.git] / embedding / browser / nsDocShellTreeOwner.cpp
blob3b1b15f69faa705e7c7dfbcbb6c753ffa8dd499c
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 // Local Includes
7 #include "nsDocShellTreeOwner.h"
8 #include "nsWebBrowser.h"
10 // Helper Classes
11 #include "nsStyleCoord.h"
12 #include "nsSize.h"
13 #include "nsHTMLReflowState.h"
14 #include "nsIServiceManager.h"
15 #include "nsComponentManagerUtils.h"
16 #include "nsXPIDLString.h"
17 #include "nsIAtom.h"
18 #include "nsReadableUtils.h"
19 #include "nsUnicharUtils.h"
20 #include "nsISimpleEnumerator.h"
21 #include "mozilla/LookAndFeel.h"
23 // Interfaces needed to be included
24 #include "nsPresContext.h"
25 #include "nsIContextMenuListener.h"
26 #include "nsIContextMenuListener2.h"
27 #include "nsITooltipListener.h"
28 #include "nsIDOMNode.h"
29 #include "nsIDOMNodeList.h"
30 #include "nsIDOMDocument.h"
31 #include "nsIDOMDocumentType.h"
32 #include "nsIDOMElement.h"
33 #include "Link.h"
34 #include "mozilla/dom/Element.h"
35 #include "mozilla/dom/SVGTitleElement.h"
36 #include "nsIDOMEvent.h"
37 #include "nsIDOMMouseEvent.h"
38 #include "nsIFormControl.h"
39 #include "nsIDOMHTMLInputElement.h"
40 #include "nsIDOMHTMLTextAreaElement.h"
41 #include "nsIDOMHTMLHtmlElement.h"
42 #include "nsIDOMHTMLAppletElement.h"
43 #include "nsIDOMHTMLObjectElement.h"
44 #include "nsIDOMHTMLEmbedElement.h"
45 #include "nsIDOMHTMLDocument.h"
46 #include "nsIImageLoadingContent.h"
47 #include "nsIWebNavigation.h"
48 #include "nsIDOMHTMLElement.h"
49 #include "nsIPresShell.h"
50 #include "nsPIDOMWindow.h"
51 #include "nsPIWindowRoot.h"
52 #include "nsIDOMWindowCollection.h"
53 #include "nsIWindowWatcher.h"
54 #include "nsPIWindowWatcher.h"
55 #include "nsIPrompt.h"
56 #include "nsRect.h"
57 #include "nsIWebBrowserChromeFocus.h"
58 #include "nsIContent.h"
59 #include "imgIContainer.h"
60 #include "nsContextMenuInfo.h"
61 #include "nsPresContext.h"
62 #include "nsViewManager.h"
63 #include "nsView.h"
64 #include "nsIDOMDragEvent.h"
65 #include "nsIConstraintValidation.h"
66 #include "mozilla/Attributes.h"
67 #include "mozilla/EventListenerManager.h"
68 #include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
70 using namespace mozilla;
71 using namespace mozilla::dom;
74 // GetEventReceiver
76 // A helper routine that navigates the tricky path from a |nsWebBrowser| to
77 // a |EventTarget| via the window root and chrome event handler.
79 static nsresult
80 GetDOMEventTarget(nsWebBrowser* inBrowser, EventTarget** aTarget)
82 NS_ENSURE_ARG_POINTER(inBrowser);
84 nsCOMPtr<nsIDOMWindow> domWindow;
85 inBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
86 NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE);
88 nsCOMPtr<nsPIDOMWindow> domWindowPrivate = do_QueryInterface(domWindow);
89 NS_ENSURE_TRUE(domWindowPrivate, NS_ERROR_FAILURE);
90 nsPIDOMWindow *rootWindow = domWindowPrivate->GetPrivateRoot();
91 NS_ENSURE_TRUE(rootWindow, NS_ERROR_FAILURE);
92 nsCOMPtr<EventTarget> target =
93 rootWindow->GetChromeEventHandler();
94 NS_ENSURE_TRUE(target, NS_ERROR_FAILURE);
95 target.forget(aTarget);
97 return NS_OK;
101 //*****************************************************************************
102 //*** nsDocShellTreeOwner: Object Management
103 //*****************************************************************************
105 nsDocShellTreeOwner::nsDocShellTreeOwner() :
106 mWebBrowser(nullptr),
107 mTreeOwner(nullptr),
108 mPrimaryContentShell(nullptr),
109 mWebBrowserChrome(nullptr),
110 mOwnerWin(nullptr),
111 mOwnerRequestor(nullptr),
112 mChromeTooltipListener(nullptr),
113 mChromeContextMenuListener(nullptr)
117 nsDocShellTreeOwner::~nsDocShellTreeOwner()
119 RemoveChromeListeners();
122 //*****************************************************************************
123 // nsDocShellTreeOwner::nsISupports
124 //*****************************************************************************
126 NS_IMPL_ADDREF(nsDocShellTreeOwner)
127 NS_IMPL_RELEASE(nsDocShellTreeOwner)
129 NS_INTERFACE_MAP_BEGIN(nsDocShellTreeOwner)
130 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocShellTreeOwner)
131 NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeOwner)
132 NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
133 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
134 NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
135 NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
136 NS_INTERFACE_MAP_ENTRY(nsICDocShellTreeOwner)
137 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
138 NS_INTERFACE_MAP_END
140 //*****************************************************************************
141 // nsDocShellTreeOwner::nsIInterfaceRequestor
142 //*****************************************************************************
144 NS_IMETHODIMP
145 nsDocShellTreeOwner::GetInterface(const nsIID& aIID, void** aSink)
147 NS_ENSURE_ARG_POINTER(aSink);
149 if(NS_SUCCEEDED(QueryInterface(aIID, aSink)))
150 return NS_OK;
152 if (aIID.Equals(NS_GET_IID(nsIWebBrowserChromeFocus))) {
153 if (mWebBrowserChromeWeak != nullptr)
154 return mWebBrowserChromeWeak->QueryReferent(aIID, aSink);
155 return mOwnerWin->QueryInterface(aIID, aSink);
158 if (aIID.Equals(NS_GET_IID(nsIPrompt))) {
159 nsIPrompt *prompt;
160 EnsurePrompter();
161 prompt = mPrompter;
162 if (prompt) {
163 NS_ADDREF(prompt);
164 *aSink = prompt;
165 return NS_OK;
167 return NS_NOINTERFACE;
170 if (aIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
171 nsIAuthPrompt *prompt;
172 EnsureAuthPrompter();
173 prompt = mAuthPrompter;
174 if (prompt) {
175 NS_ADDREF(prompt);
176 *aSink = prompt;
177 return NS_OK;
179 return NS_NOINTERFACE;
182 nsCOMPtr<nsIInterfaceRequestor> req = GetOwnerRequestor();
183 if (req)
184 return req->GetInterface(aIID, aSink);
186 return NS_NOINTERFACE;
189 //*****************************************************************************
190 // nsDocShellTreeOwner::nsIDocShellTreeOwner
191 //*****************************************************************************
193 NS_IMETHODIMP
194 nsDocShellTreeOwner::FindItemWithName(const char16_t* aName,
195 nsIDocShellTreeItem* aRequestor,
196 nsIDocShellTreeItem* aOriginalRequestor,
197 nsIDocShellTreeItem** aFoundItem)
199 NS_ENSURE_ARG(aName);
200 NS_ENSURE_ARG_POINTER(aFoundItem);
201 *aFoundItem = nullptr; // if we don't find one, we return NS_OK and a null result
202 nsresult rv;
204 nsAutoString name(aName);
206 if (!mWebBrowser)
207 return NS_OK; // stymied
209 /* special cases */
210 if(name.IsEmpty())
211 return NS_OK;
212 if(name.LowerCaseEqualsLiteral("_blank"))
213 return NS_OK;
214 // _main is an IE target which should be case-insensitive but isn't
215 // see bug 217886 for details
216 // XXXbz what if our browser isn't targetable? We need to handle that somehow.
217 if(name.LowerCaseEqualsLiteral("_content") || name.EqualsLiteral("_main")) {
218 *aFoundItem = mWebBrowser->mDocShell;
219 NS_IF_ADDREF(*aFoundItem);
220 return NS_OK;
223 if (!SameCOMIdentity(aRequestor, mWebBrowser->mDocShell)) {
224 // This isn't a request coming up from our kid, so check with said kid
225 nsISupports* thisSupports = static_cast<nsIDocShellTreeOwner*>(this);
226 rv = mWebBrowser->mDocShell->FindItemWithName(aName, thisSupports,
227 aOriginalRequestor, aFoundItem);
228 if (NS_FAILED(rv) || *aFoundItem) {
229 return rv;
233 // next, if we have a parent and it isn't the requestor, ask it
234 if(mTreeOwner) {
235 nsCOMPtr<nsIDocShellTreeOwner> reqAsTreeOwner(do_QueryInterface(aRequestor));
236 if (mTreeOwner != reqAsTreeOwner)
237 return mTreeOwner->FindItemWithName(aName, mWebBrowser->mDocShell,
238 aOriginalRequestor, aFoundItem);
239 return NS_OK;
242 // finally, failing everything else, search all windows
243 return FindItemWithNameAcrossWindows(aName, aRequestor, aOriginalRequestor,
244 aFoundItem);
247 nsresult
248 nsDocShellTreeOwner::FindItemWithNameAcrossWindows(const char16_t* aName,
249 nsIDocShellTreeItem* aRequestor,
250 nsIDocShellTreeItem* aOriginalRequestor,
251 nsIDocShellTreeItem** aFoundItem)
253 // search for the item across the list of top-level windows
254 nsCOMPtr<nsPIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
255 if (!wwatch)
256 return NS_OK;
258 return wwatch->FindItemWithName(aName, aRequestor, aOriginalRequestor,
259 aFoundItem);
262 void
263 nsDocShellTreeOwner::EnsurePrompter()
265 if (mPrompter)
266 return;
268 nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
269 if (wwatch && mWebBrowser) {
270 nsCOMPtr<nsIDOMWindow> domWindow;
271 mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
272 if (domWindow)
273 wwatch->GetNewPrompter(domWindow, getter_AddRefs(mPrompter));
277 void
278 nsDocShellTreeOwner::EnsureAuthPrompter()
280 if (mAuthPrompter)
281 return;
283 nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
284 if (wwatch && mWebBrowser) {
285 nsCOMPtr<nsIDOMWindow> domWindow;
286 mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
287 if (domWindow)
288 wwatch->GetNewAuthPrompter(domWindow, getter_AddRefs(mAuthPrompter));
292 void
293 nsDocShellTreeOwner::AddToWatcher()
295 if (mWebBrowser) {
296 nsCOMPtr<nsIDOMWindow> domWindow;
297 mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
298 if (domWindow) {
299 nsCOMPtr<nsPIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
300 if (wwatch) {
301 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
302 if (webBrowserChrome)
303 wwatch->AddWindow(domWindow, webBrowserChrome);
309 void
310 nsDocShellTreeOwner::RemoveFromWatcher()
312 if (mWebBrowser) {
313 nsCOMPtr<nsIDOMWindow> domWindow;
314 mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
315 if (domWindow) {
316 nsCOMPtr<nsPIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
317 if (wwatch)
318 wwatch->RemoveWindow(domWindow);
324 NS_IMETHODIMP
325 nsDocShellTreeOwner::ContentShellAdded(nsIDocShellTreeItem* aContentShell,
326 bool aPrimary, bool aTargetable,
327 const nsAString& aID)
329 if(mTreeOwner)
330 return mTreeOwner->ContentShellAdded(aContentShell, aPrimary,
331 aTargetable, aID);
333 if (aPrimary)
334 mPrimaryContentShell = aContentShell;
335 return NS_OK;
338 NS_IMETHODIMP
339 nsDocShellTreeOwner::ContentShellRemoved(nsIDocShellTreeItem* aContentShell)
341 if(mTreeOwner)
342 return mTreeOwner->ContentShellRemoved(aContentShell);
344 if(mPrimaryContentShell == aContentShell)
345 mPrimaryContentShell = nullptr;
347 return NS_OK;
350 NS_IMETHODIMP
351 nsDocShellTreeOwner::GetPrimaryContentShell(nsIDocShellTreeItem** aShell)
353 NS_ENSURE_ARG_POINTER(aShell);
355 if (mTreeOwner)
356 return mTreeOwner->GetPrimaryContentShell(aShell);
358 *aShell = (mPrimaryContentShell ? mPrimaryContentShell : mWebBrowser->mDocShell);
359 NS_IF_ADDREF(*aShell);
361 return NS_OK;
364 NS_IMETHODIMP
365 nsDocShellTreeOwner::SizeShellTo(nsIDocShellTreeItem* aShellItem,
366 int32_t aCX, int32_t aCY)
368 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
370 NS_ENSURE_STATE(mTreeOwner || webBrowserChrome);
372 if(mTreeOwner)
373 return mTreeOwner->SizeShellTo(aShellItem, aCX, aCY);
375 if(aShellItem == mWebBrowser->mDocShell)
376 return webBrowserChrome->SizeBrowserTo(aCX, aCY);
378 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(aShellItem));
379 NS_ENSURE_TRUE(webNav, NS_ERROR_FAILURE);
381 nsCOMPtr<nsIDOMDocument> domDocument;
382 webNav->GetDocument(getter_AddRefs(domDocument));
383 NS_ENSURE_TRUE(domDocument, NS_ERROR_FAILURE);
385 nsCOMPtr<nsIDOMElement> domElement;
386 domDocument->GetDocumentElement(getter_AddRefs(domElement));
387 NS_ENSURE_TRUE(domElement, NS_ERROR_FAILURE);
389 // Set the preferred Size
390 //XXX
391 NS_ERROR("Implement this");
393 Set the preferred size on the aShellItem.
396 nsRefPtr<nsPresContext> presContext;
397 mWebBrowser->mDocShell->GetPresContext(getter_AddRefs(presContext));
398 NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
400 nsIPresShell *presShell = presContext->GetPresShell();
401 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
403 NS_ENSURE_SUCCESS(presShell->ResizeReflow(NS_UNCONSTRAINEDSIZE,
404 NS_UNCONSTRAINEDSIZE), NS_ERROR_FAILURE);
406 nsRect shellArea = presContext->GetVisibleArea();
408 int32_t browserCX = presContext->AppUnitsToDevPixels(shellArea.width);
409 int32_t browserCY = presContext->AppUnitsToDevPixels(shellArea.height);
411 return webBrowserChrome->SizeBrowserTo(browserCX, browserCY);
414 NS_IMETHODIMP
415 nsDocShellTreeOwner::SetPersistence(bool aPersistPosition,
416 bool aPersistSize,
417 bool aPersistSizeMode)
419 return NS_ERROR_NOT_IMPLEMENTED;
422 NS_IMETHODIMP
423 nsDocShellTreeOwner::GetPersistence(bool* aPersistPosition,
424 bool* aPersistSize,
425 bool* aPersistSizeMode)
427 return NS_ERROR_NOT_IMPLEMENTED;
430 NS_IMETHODIMP
431 nsDocShellTreeOwner::GetTargetableShellCount(uint32_t* aResult)
433 if(mTreeOwner) {
434 mTreeOwner->GetTargetableShellCount(aResult);
435 } else {
436 *aResult = 0;
439 return NS_OK;
442 //*****************************************************************************
443 // nsDocShellTreeOwner::nsIBaseWindow
444 //*****************************************************************************
447 NS_IMETHODIMP
448 nsDocShellTreeOwner::InitWindow(nativeWindow aParentNativeWindow,
449 nsIWidget* aParentWidget, int32_t aX,
450 int32_t aY, int32_t aCX, int32_t aCY)
452 return NS_ERROR_NULL_POINTER;
455 NS_IMETHODIMP
456 nsDocShellTreeOwner::Create()
458 return NS_ERROR_NULL_POINTER;
461 NS_IMETHODIMP
462 nsDocShellTreeOwner::Destroy()
464 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
465 if (webBrowserChrome)
467 return webBrowserChrome->DestroyBrowserWindow();
470 return NS_ERROR_NULL_POINTER;
473 NS_IMETHODIMP
474 nsDocShellTreeOwner::GetUnscaledDevicePixelsPerCSSPixel(double *aScale)
476 if (mWebBrowser) {
477 return mWebBrowser->GetUnscaledDevicePixelsPerCSSPixel(aScale);
480 *aScale = 1.0;
481 return NS_OK;
484 NS_IMETHODIMP
485 nsDocShellTreeOwner::SetPosition(int32_t aX, int32_t aY)
487 nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
488 if (ownerWin)
490 return ownerWin->SetDimensions(nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION,
491 aX, aY, 0, 0);
493 return NS_ERROR_NULL_POINTER;
496 NS_IMETHODIMP
497 nsDocShellTreeOwner::GetPosition(int32_t* aX, int32_t* aY)
499 nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
500 if (ownerWin)
502 return ownerWin->GetDimensions(nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION,
503 aX, aY, nullptr, nullptr);
505 return NS_ERROR_NULL_POINTER;
508 NS_IMETHODIMP
509 nsDocShellTreeOwner::SetSize(int32_t aCX, int32_t aCY, bool aRepaint)
511 nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
512 if (ownerWin)
514 return ownerWin->SetDimensions(nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_OUTER,
515 0, 0, aCX, aCY);
517 return NS_ERROR_NULL_POINTER;
520 NS_IMETHODIMP
521 nsDocShellTreeOwner::GetSize(int32_t* aCX, int32_t* aCY)
523 nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
524 if (ownerWin)
526 return ownerWin->GetDimensions(nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_OUTER,
527 nullptr, nullptr, aCX, aCY);
529 return NS_ERROR_NULL_POINTER;
532 NS_IMETHODIMP
533 nsDocShellTreeOwner::SetPositionAndSize(int32_t aX, int32_t aY, int32_t aCX,
534 int32_t aCY, bool aRepaint)
536 nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
537 if (ownerWin)
539 return ownerWin->SetDimensions(nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_OUTER |
540 nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION,
541 aX, aY, aCX, aCY);
543 return NS_ERROR_NULL_POINTER;
546 NS_IMETHODIMP
547 nsDocShellTreeOwner::GetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aCX,
548 int32_t* aCY)
550 nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
551 if (ownerWin)
553 return ownerWin->GetDimensions(nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_OUTER |
554 nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION,
555 aX, aY, aCX, aCY);
557 return NS_ERROR_NULL_POINTER;
560 NS_IMETHODIMP
561 nsDocShellTreeOwner::Repaint(bool aForce)
563 return NS_ERROR_NULL_POINTER;
566 NS_IMETHODIMP
567 nsDocShellTreeOwner::GetParentWidget(nsIWidget** aParentWidget)
569 return NS_ERROR_NULL_POINTER;
572 NS_IMETHODIMP
573 nsDocShellTreeOwner::SetParentWidget(nsIWidget* aParentWidget)
575 return NS_ERROR_NULL_POINTER;
578 NS_IMETHODIMP
579 nsDocShellTreeOwner::GetParentNativeWindow(nativeWindow* aParentNativeWindow)
581 nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
582 if (ownerWin)
584 return ownerWin->GetSiteWindow(aParentNativeWindow);
586 return NS_ERROR_NULL_POINTER;
589 NS_IMETHODIMP
590 nsDocShellTreeOwner::SetParentNativeWindow(nativeWindow aParentNativeWindow)
592 return NS_ERROR_NULL_POINTER;
595 NS_IMETHODIMP
596 nsDocShellTreeOwner::GetNativeHandle(nsAString& aNativeHandle)
598 // the nativeHandle should be accessed from nsIXULWindow
599 return NS_ERROR_NOT_IMPLEMENTED;
602 NS_IMETHODIMP
603 nsDocShellTreeOwner::GetVisibility(bool* aVisibility)
605 nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
606 if (ownerWin)
608 return ownerWin->GetVisibility(aVisibility);
610 return NS_ERROR_NULL_POINTER;
613 NS_IMETHODIMP
614 nsDocShellTreeOwner::SetVisibility(bool aVisibility)
616 nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
617 if (ownerWin)
619 return ownerWin->SetVisibility(aVisibility);
621 return NS_ERROR_NULL_POINTER;
624 NS_IMETHODIMP
625 nsDocShellTreeOwner::GetEnabled(bool *aEnabled)
627 NS_ENSURE_ARG_POINTER(aEnabled);
628 *aEnabled = true;
629 return NS_ERROR_NOT_IMPLEMENTED;
632 NS_IMETHODIMP
633 nsDocShellTreeOwner::SetEnabled(bool aEnabled)
635 return NS_ERROR_NOT_IMPLEMENTED;
638 NS_IMETHODIMP
639 nsDocShellTreeOwner::GetMainWidget(nsIWidget** aMainWidget)
641 return NS_ERROR_NULL_POINTER;
644 NS_IMETHODIMP
645 nsDocShellTreeOwner::SetFocus()
647 nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
648 if (ownerWin)
650 return ownerWin->SetFocus();
652 return NS_ERROR_NULL_POINTER;
655 NS_IMETHODIMP
656 nsDocShellTreeOwner::GetTitle(char16_t** aTitle)
658 nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
659 if (ownerWin)
661 return ownerWin->GetTitle(aTitle);
663 return NS_ERROR_NULL_POINTER;
666 NS_IMETHODIMP
667 nsDocShellTreeOwner::SetTitle(const char16_t* aTitle)
669 nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
670 if (ownerWin)
672 return ownerWin->SetTitle(aTitle);
674 return NS_ERROR_NULL_POINTER;
678 //*****************************************************************************
679 // nsDocShellTreeOwner::nsIWebProgressListener
680 //*****************************************************************************
683 NS_IMETHODIMP
684 nsDocShellTreeOwner::OnProgressChange(nsIWebProgress* aProgress,
685 nsIRequest* aRequest,
686 int32_t aCurSelfProgress,
687 int32_t aMaxSelfProgress,
688 int32_t aCurTotalProgress,
689 int32_t aMaxTotalProgress)
691 // In the absence of DOM document creation event, this method is the
692 // most convenient place to install the mouse listener on the
693 // DOM document.
694 return AddChromeListeners();
697 NS_IMETHODIMP
698 nsDocShellTreeOwner::OnStateChange(nsIWebProgress* aProgress,
699 nsIRequest* aRequest,
700 uint32_t aProgressStateFlags,
701 nsresult aStatus)
703 return NS_OK;
706 NS_IMETHODIMP
707 nsDocShellTreeOwner::OnLocationChange(nsIWebProgress* aWebProgress,
708 nsIRequest* aRequest,
709 nsIURI* aURI,
710 uint32_t aFlags)
712 return NS_OK;
715 NS_IMETHODIMP
716 nsDocShellTreeOwner::OnStatusChange(nsIWebProgress* aWebProgress,
717 nsIRequest* aRequest,
718 nsresult aStatus,
719 const char16_t* aMessage)
721 return NS_OK;
724 NS_IMETHODIMP
725 nsDocShellTreeOwner::OnSecurityChange(nsIWebProgress *aWebProgress,
726 nsIRequest *aRequest,
727 uint32_t state)
729 return NS_OK;
733 //*****************************************************************************
734 // nsDocShellTreeOwner: Helpers
735 //*****************************************************************************
737 //*****************************************************************************
738 // nsDocShellTreeOwner: Accessors
739 //*****************************************************************************
741 void
742 nsDocShellTreeOwner::WebBrowser(nsWebBrowser* aWebBrowser)
744 if ( !aWebBrowser )
745 RemoveChromeListeners();
746 if (aWebBrowser != mWebBrowser) {
747 mPrompter = 0;
748 mAuthPrompter = 0;
751 mWebBrowser = aWebBrowser;
754 nsWebBrowser *
755 nsDocShellTreeOwner::WebBrowser()
757 return mWebBrowser;
760 NS_IMETHODIMP
761 nsDocShellTreeOwner::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner)
763 if(aTreeOwner) {
764 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome(do_GetInterface(aTreeOwner));
765 NS_ENSURE_TRUE(webBrowserChrome, NS_ERROR_INVALID_ARG);
766 NS_ENSURE_SUCCESS(SetWebBrowserChrome(webBrowserChrome), NS_ERROR_INVALID_ARG);
767 mTreeOwner = aTreeOwner;
769 else {
770 mTreeOwner = nullptr;
771 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
772 if (!webBrowserChrome)
773 NS_ENSURE_SUCCESS(SetWebBrowserChrome(nullptr), NS_ERROR_FAILURE);
776 return NS_OK;
779 NS_IMETHODIMP
780 nsDocShellTreeOwner::SetWebBrowserChrome(nsIWebBrowserChrome* aWebBrowserChrome)
782 if(!aWebBrowserChrome) {
783 mWebBrowserChrome = nullptr;
784 mOwnerWin = nullptr;
785 mOwnerRequestor = nullptr;
786 mWebBrowserChromeWeak = 0;
787 } else {
788 nsCOMPtr<nsISupportsWeakReference> supportsweak =
789 do_QueryInterface(aWebBrowserChrome);
790 if (supportsweak) {
791 supportsweak->GetWeakReference(getter_AddRefs(mWebBrowserChromeWeak));
792 } else {
793 nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin(do_QueryInterface(aWebBrowserChrome));
794 nsCOMPtr<nsIInterfaceRequestor> requestor(do_QueryInterface(aWebBrowserChrome));
796 // it's ok for ownerWin or requestor to be null.
797 mWebBrowserChrome = aWebBrowserChrome;
798 mOwnerWin = ownerWin;
799 mOwnerRequestor = requestor;
802 return NS_OK;
807 // AddChromeListeners
809 // Hook up things to the chrome like context menus and tooltips, if the chrome
810 // has implemented the right interfaces.
812 NS_IMETHODIMP
813 nsDocShellTreeOwner::AddChromeListeners()
815 nsresult rv = NS_OK;
817 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
818 if (!webBrowserChrome)
819 return NS_ERROR_FAILURE;
821 // install tooltips
822 if ( !mChromeTooltipListener ) {
823 nsCOMPtr<nsITooltipListener>
824 tooltipListener(do_QueryInterface(webBrowserChrome));
825 if ( tooltipListener ) {
826 mChromeTooltipListener = new ChromeTooltipListener(mWebBrowser,
827 webBrowserChrome);
828 if ( mChromeTooltipListener ) {
829 NS_ADDREF(mChromeTooltipListener);
830 rv = mChromeTooltipListener->AddChromeListeners();
832 else
833 rv = NS_ERROR_OUT_OF_MEMORY;
837 // install context menus
838 if ( !mChromeContextMenuListener ) {
839 nsCOMPtr<nsIContextMenuListener2>
840 contextListener2(do_QueryInterface(webBrowserChrome));
841 nsCOMPtr<nsIContextMenuListener>
842 contextListener(do_QueryInterface(webBrowserChrome));
843 if ( contextListener2 || contextListener ) {
844 mChromeContextMenuListener =
845 new ChromeContextMenuListener(mWebBrowser, webBrowserChrome);
846 if ( mChromeContextMenuListener ) {
847 NS_ADDREF(mChromeContextMenuListener);
848 rv = mChromeContextMenuListener->AddChromeListeners();
850 else
851 rv = NS_ERROR_OUT_OF_MEMORY;
855 // register dragover and drop event listeners with the listener manager
856 nsCOMPtr<EventTarget> target;
857 GetDOMEventTarget(mWebBrowser, getter_AddRefs(target));
859 EventListenerManager* elmP = target->GetOrCreateListenerManager();
860 if (elmP) {
861 elmP->AddEventListenerByType(this, NS_LITERAL_STRING("dragover"),
862 TrustedEventsAtSystemGroupBubble());
863 elmP->AddEventListenerByType(this, NS_LITERAL_STRING("drop"),
864 TrustedEventsAtSystemGroupBubble());
867 return rv;
869 } // AddChromeListeners
872 NS_IMETHODIMP
873 nsDocShellTreeOwner::RemoveChromeListeners()
875 if ( mChromeTooltipListener ) {
876 mChromeTooltipListener->RemoveChromeListeners();
877 NS_RELEASE(mChromeTooltipListener);
879 if ( mChromeContextMenuListener ) {
880 mChromeContextMenuListener->RemoveChromeListeners();
881 NS_RELEASE(mChromeContextMenuListener);
884 nsCOMPtr<EventTarget> piTarget;
885 GetDOMEventTarget(mWebBrowser, getter_AddRefs(piTarget));
886 if (!piTarget)
887 return NS_OK;
889 EventListenerManager* elmP = piTarget->GetOrCreateListenerManager();
890 if (elmP)
892 elmP->RemoveEventListenerByType(this, NS_LITERAL_STRING("dragover"),
893 TrustedEventsAtSystemGroupBubble());
894 elmP->RemoveEventListenerByType(this, NS_LITERAL_STRING("drop"),
895 TrustedEventsAtSystemGroupBubble());
898 return NS_OK;
901 NS_IMETHODIMP
902 nsDocShellTreeOwner::HandleEvent(nsIDOMEvent* aEvent)
904 nsCOMPtr<nsIDOMDragEvent> dragEvent = do_QueryInterface(aEvent);
905 NS_ENSURE_TRUE(dragEvent, NS_ERROR_INVALID_ARG);
907 bool defaultPrevented;
908 aEvent->GetDefaultPrevented(&defaultPrevented);
909 if (defaultPrevented) {
910 return NS_OK;
913 nsCOMPtr<nsIDroppedLinkHandler> handler = do_GetService("@mozilla.org/content/dropped-link-handler;1");
914 if (handler) {
915 nsAutoString eventType;
916 aEvent->GetType(eventType);
917 if (eventType.EqualsLiteral("dragover")) {
918 bool canDropLink;
919 handler->CanDropLink(dragEvent, false, &canDropLink);
920 if (canDropLink)
921 aEvent->PreventDefault();
923 else if (eventType.EqualsLiteral("drop")) {
924 nsIWebNavigation* webnav = static_cast<nsIWebNavigation *>(mWebBrowser);
926 nsAutoString link, name;
927 if (webnav && NS_SUCCEEDED(handler->DropLink(dragEvent, link, false, name))) {
928 if (!link.IsEmpty()) {
929 webnav->LoadURI(link.get(), 0, nullptr, nullptr, nullptr);
932 else {
933 aEvent->StopPropagation();
934 aEvent->PreventDefault();
939 return NS_OK;
942 already_AddRefed<nsIWebBrowserChrome>
943 nsDocShellTreeOwner::GetWebBrowserChrome()
945 nsCOMPtr<nsIWebBrowserChrome> chrome;
946 if (mWebBrowserChromeWeak) {
947 chrome = do_QueryReferent(mWebBrowserChromeWeak);
948 } else if (mWebBrowserChrome) {
949 chrome = mWebBrowserChrome;
951 return chrome.forget();
954 already_AddRefed<nsIEmbeddingSiteWindow>
955 nsDocShellTreeOwner::GetOwnerWin()
957 nsCOMPtr<nsIEmbeddingSiteWindow> win;
958 if (mWebBrowserChromeWeak) {
959 win = do_QueryReferent(mWebBrowserChromeWeak);
960 } else if (mOwnerWin) {
961 win = mOwnerWin;
963 return win.forget();
966 already_AddRefed<nsIInterfaceRequestor>
967 nsDocShellTreeOwner::GetOwnerRequestor()
969 nsCOMPtr<nsIInterfaceRequestor> req;
970 if (mWebBrowserChromeWeak) {
971 req = do_QueryReferent(mWebBrowserChromeWeak);
972 } else if (mOwnerRequestor) {
973 req = mOwnerRequestor;
975 return req.forget();
979 ///////////////////////////////////////////////////////////////////////////////
980 // DefaultTooltipTextProvider
982 class DefaultTooltipTextProvider MOZ_FINAL : public nsITooltipTextProvider
984 public:
985 DefaultTooltipTextProvider();
987 NS_DECL_THREADSAFE_ISUPPORTS
988 NS_DECL_NSITOOLTIPTEXTPROVIDER
990 protected:
991 ~DefaultTooltipTextProvider() {}
993 nsCOMPtr<nsIAtom> mTag_dialog;
994 nsCOMPtr<nsIAtom> mTag_dialogheader;
995 nsCOMPtr<nsIAtom> mTag_window;
998 NS_IMPL_ISUPPORTS(DefaultTooltipTextProvider, nsITooltipTextProvider)
1000 DefaultTooltipTextProvider::DefaultTooltipTextProvider()
1002 // There are certain element types which we don't want to use
1003 // as tool tip text.
1004 mTag_dialog = do_GetAtom("dialog");
1005 mTag_dialogheader = do_GetAtom("dialogheader");
1006 mTag_window = do_GetAtom("window");
1010 // UseSVGTitle
1012 // A helper routine that determines whether we're still interested
1013 // in SVG titles. We need to stop at the SVG root element that
1014 // has a document node parent
1016 static bool
1017 UseSVGTitle(nsIDOMElement *currElement)
1019 nsCOMPtr<dom::Element> element(do_QueryInterface(currElement));
1020 if (!element || !element->IsSVG() || !element->GetParentNode())
1021 return false;
1023 return element->GetParentNode()->NodeType() != nsIDOMNode::DOCUMENT_NODE;
1026 /* void getNodeText (in nsIDOMNode aNode, out wstring aText); */
1027 NS_IMETHODIMP
1028 DefaultTooltipTextProvider::GetNodeText(nsIDOMNode *aNode, char16_t **aText,
1029 bool *_retval)
1031 NS_ENSURE_ARG_POINTER(aNode);
1032 NS_ENSURE_ARG_POINTER(aText);
1034 nsString outText;
1036 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
1038 bool lookingForSVGTitle = true;
1039 bool found = false;
1040 nsCOMPtr<nsIDOMNode> current ( aNode );
1042 // If the element implement the constraint validation API and has no title,
1043 // show the validation message, if any.
1044 nsCOMPtr<nsIConstraintValidation> cvElement = do_QueryInterface(current);
1045 if (cvElement) {
1046 nsCOMPtr<nsIContent> content = do_QueryInterface(cvElement);
1047 nsCOMPtr<nsIAtom> titleAtom = do_GetAtom("title");
1049 nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(content);
1050 bool formHasNoValidate = false;
1051 mozilla::dom::Element* form = formControl->GetFormElement();
1052 if (form) {
1053 nsCOMPtr<nsIAtom> noValidateAtom = do_GetAtom("novalidate");
1054 formHasNoValidate = form->HasAttr(kNameSpaceID_None, noValidateAtom);
1057 if (!content->HasAttr(kNameSpaceID_None, titleAtom) &&
1058 !formHasNoValidate) {
1059 cvElement->GetValidationMessage(outText);
1060 found = !outText.IsEmpty();
1064 while ( !found && current ) {
1065 nsCOMPtr<nsIDOMElement> currElement ( do_QueryInterface(current) );
1066 if ( currElement ) {
1067 nsCOMPtr<nsIContent> content(do_QueryInterface(currElement));
1068 if (content) {
1069 nsIAtom *tagAtom = content->Tag();
1070 if (tagAtom != mTag_dialog &&
1071 tagAtom != mTag_dialogheader &&
1072 tagAtom != mTag_window) {
1073 // first try the normal title attribute...
1074 currElement->GetAttribute(NS_LITERAL_STRING("title"), outText);
1075 if ( outText.Length() )
1076 found = true;
1077 else {
1078 // ...ok, that didn't work, try it in the XLink namespace
1079 NS_NAMED_LITERAL_STRING(xlinkNS, "http://www.w3.org/1999/xlink");
1080 nsCOMPtr<mozilla::dom::Link> linkContent(do_QueryInterface(currElement));
1081 if (linkContent) {
1082 nsCOMPtr<nsIURI> uri(linkContent->GetURIExternal());
1083 if (uri) {
1084 currElement->GetAttributeNS(NS_LITERAL_STRING("http://www.w3.org/1999/xlink"), NS_LITERAL_STRING("title"), outText);
1085 if ( outText.Length() )
1086 found = true;
1089 else {
1090 if (lookingForSVGTitle) {
1091 lookingForSVGTitle = UseSVGTitle(currElement);
1093 if (lookingForSVGTitle) {
1094 nsINodeList* childNodes = node->ChildNodes();
1095 uint32_t childNodeCount = childNodes->Length();
1096 for (uint32_t i = 0; i < childNodeCount; i++) {
1097 nsIContent* child = childNodes->Item(i);
1098 if (child->IsSVG(nsGkAtoms::title)) {
1099 static_cast<dom::SVGTitleElement*>(child)->GetTextContent(outText);
1100 if ( outText.Length() )
1101 found = true;
1102 break;
1112 // not found here, walk up to the parent and keep trying
1113 if ( !found ) {
1114 nsCOMPtr<nsIDOMNode> temp ( current );
1115 temp->GetParentNode(getter_AddRefs(current));
1117 } // while not found
1119 *_retval = found;
1120 *aText = (found) ? ToNewUnicode(outText) : nullptr;
1122 return NS_OK;
1125 ///////////////////////////////////////////////////////////////////////////////
1127 NS_IMPL_ISUPPORTS(ChromeTooltipListener, nsIDOMEventListener)
1130 // ChromeTooltipListener ctor
1133 ChromeTooltipListener::ChromeTooltipListener(nsWebBrowser* inBrowser,
1134 nsIWebBrowserChrome* inChrome)
1135 : mWebBrowser(inBrowser), mWebBrowserChrome(inChrome),
1136 mTooltipListenerInstalled(false),
1137 mMouseClientX(0), mMouseClientY(0),
1138 mShowingTooltip(false)
1140 mTooltipTextProvider = do_GetService(NS_TOOLTIPTEXTPROVIDER_CONTRACTID);
1141 if (!mTooltipTextProvider) {
1142 nsISupports *pProvider = (nsISupports *) new DefaultTooltipTextProvider;
1143 mTooltipTextProvider = do_QueryInterface(pProvider);
1145 } // ctor
1149 // ChromeTooltipListener dtor
1151 ChromeTooltipListener::~ChromeTooltipListener()
1154 } // dtor
1158 // AddChromeListeners
1160 // Hook up things to the chrome like context menus and tooltips, if the chrome
1161 // has implemented the right interfaces.
1163 NS_IMETHODIMP
1164 ChromeTooltipListener::AddChromeListeners()
1166 if (!mEventTarget)
1167 GetDOMEventTarget(mWebBrowser, getter_AddRefs(mEventTarget));
1169 // Register the appropriate events for tooltips, but only if
1170 // the embedding chrome cares.
1171 nsresult rv = NS_OK;
1172 nsCOMPtr<nsITooltipListener> tooltipListener ( do_QueryInterface(mWebBrowserChrome) );
1173 if ( tooltipListener && !mTooltipListenerInstalled ) {
1174 rv = AddTooltipListener();
1175 if ( NS_FAILED(rv) )
1176 return rv;
1179 return rv;
1181 } // AddChromeListeners
1185 // AddTooltipListener
1187 // Subscribe to the events that will allow us to track tooltips. We need "mouse" for mouseExit,
1188 // "mouse motion" for mouseMove, and "key" for keyDown. As we add the listeners, keep track
1189 // of how many succeed so we can clean up correctly in Release().
1191 NS_IMETHODIMP
1192 ChromeTooltipListener::AddTooltipListener()
1194 if (mEventTarget) {
1195 nsresult rv = mEventTarget->AddEventListener(NS_LITERAL_STRING("keydown"),
1196 this, false, false);
1197 NS_ENSURE_SUCCESS(rv, rv);
1198 rv = mEventTarget->AddEventListener(NS_LITERAL_STRING("mousedown"), this,
1199 false, false);
1200 NS_ENSURE_SUCCESS(rv, rv);
1201 rv = mEventTarget->AddEventListener(NS_LITERAL_STRING("mouseout"), this,
1202 false, false);
1203 NS_ENSURE_SUCCESS(rv, rv);
1204 rv = mEventTarget->AddEventListener(NS_LITERAL_STRING("mousemove"), this,
1205 false, false);
1206 NS_ENSURE_SUCCESS(rv, rv);
1208 mTooltipListenerInstalled = true;
1211 return NS_OK;
1216 // RemoveChromeListeners
1218 // Unsubscribe from the various things we've hooked up to the window root.
1220 NS_IMETHODIMP
1221 ChromeTooltipListener::RemoveChromeListeners ( )
1223 HideTooltip();
1225 if ( mTooltipListenerInstalled )
1226 RemoveTooltipListener();
1228 mEventTarget = nullptr;
1230 // it really doesn't matter if these fail...
1231 return NS_OK;
1233 } // RemoveChromeTooltipListeners
1238 // RemoveTooltipListener
1240 // Unsubscribe from all the various tooltip events that we were listening to
1242 NS_IMETHODIMP
1243 ChromeTooltipListener::RemoveTooltipListener()
1245 if (mEventTarget) {
1246 nsresult rv =
1247 mEventTarget->RemoveEventListener(NS_LITERAL_STRING("keydown"), this,
1248 false);
1249 NS_ENSURE_SUCCESS(rv, rv);
1250 rv = mEventTarget->RemoveEventListener(NS_LITERAL_STRING("mousedown"),
1251 this, false);
1252 NS_ENSURE_SUCCESS(rv, rv);
1253 rv = mEventTarget->RemoveEventListener(NS_LITERAL_STRING("mouseout"), this,
1254 false);
1255 NS_ENSURE_SUCCESS(rv, rv);
1256 rv = mEventTarget->RemoveEventListener(NS_LITERAL_STRING("mousemove"),
1257 this, false);
1258 NS_ENSURE_SUCCESS(rv, rv);
1260 mTooltipListenerInstalled = false;
1263 return NS_OK;
1266 NS_IMETHODIMP
1267 ChromeTooltipListener::HandleEvent(nsIDOMEvent* aEvent)
1269 nsAutoString eventType;
1270 aEvent->GetType(eventType);
1272 if (eventType.EqualsLiteral("keydown") ||
1273 eventType.EqualsLiteral("mousedown") ||
1274 eventType.EqualsLiteral("mouseout"))
1275 return HideTooltip();
1276 if (eventType.EqualsLiteral("mousemove"))
1277 return MouseMove(aEvent);
1279 NS_ERROR("Unexpected event type");
1280 return NS_OK;
1284 // MouseMove
1286 // If we're a tooltip, fire off a timer to see if a tooltip should be shown. If the
1287 // timer fires, we cache the node in |mPossibleTooltipNode|.
1289 nsresult
1290 ChromeTooltipListener::MouseMove(nsIDOMEvent* aMouseEvent)
1292 nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(aMouseEvent) );
1293 if (!mouseEvent)
1294 return NS_OK;
1296 // stash the coordinates of the event so that we can still get back to it from within the
1297 // timer callback. On win32, we'll get a MouseMove event even when a popup goes away --
1298 // even when the mouse doesn't change position! To get around this, we make sure the
1299 // mouse has really moved before proceeding.
1300 int32_t newMouseX, newMouseY;
1301 mouseEvent->GetClientX(&newMouseX);
1302 mouseEvent->GetClientY(&newMouseY);
1303 if ( mMouseClientX == newMouseX && mMouseClientY == newMouseY )
1304 return NS_OK;
1305 mMouseClientX = newMouseX; mMouseClientY = newMouseY;
1306 mouseEvent->GetScreenX(&mMouseScreenX);
1307 mouseEvent->GetScreenY(&mMouseScreenY);
1309 // We want to close the tip if it is being displayed and the mouse moves. Recall
1310 // that |mShowingTooltip| is set when the popup is showing. Furthermore, as the mouse
1311 // moves, we want to make sure we reset the timer to show it, so that the delay
1312 // is from when the mouse stops moving, not when it enters the element.
1313 if ( mShowingTooltip )
1314 return HideTooltip();
1315 if ( mTooltipTimer )
1316 mTooltipTimer->Cancel();
1318 mTooltipTimer = do_CreateInstance("@mozilla.org/timer;1");
1319 if ( mTooltipTimer ) {
1320 nsCOMPtr<EventTarget> eventTarget = aMouseEvent->InternalDOMEvent()->GetTarget();
1321 if ( eventTarget )
1322 mPossibleTooltipNode = do_QueryInterface(eventTarget);
1323 if ( mPossibleTooltipNode ) {
1324 nsresult rv =
1325 mTooltipTimer->InitWithFuncCallback(sTooltipCallback, this,
1326 LookAndFeel::GetInt(LookAndFeel::eIntID_TooltipDelay, 500),
1327 nsITimer::TYPE_ONE_SHOT);
1328 if (NS_FAILED(rv))
1329 mPossibleTooltipNode = nullptr;
1332 else
1333 NS_WARNING ( "Could not create a timer for tooltip tracking" );
1335 return NS_OK;
1337 } // MouseMove
1341 // ShowTooltip
1343 // Tell the registered chrome that they should show the tooltip
1345 NS_IMETHODIMP
1346 ChromeTooltipListener::ShowTooltip(int32_t inXCoords, int32_t inYCoords,
1347 const nsAString & inTipText)
1349 nsresult rv = NS_OK;
1351 // do the work to call the client
1352 nsCOMPtr<nsITooltipListener> tooltipListener ( do_QueryInterface(mWebBrowserChrome) );
1353 if ( tooltipListener ) {
1354 rv = tooltipListener->OnShowTooltip ( inXCoords, inYCoords, PromiseFlatString(inTipText).get() );
1355 if ( NS_SUCCEEDED(rv) )
1356 mShowingTooltip = true;
1359 return rv;
1361 } // ShowTooltip
1365 // HideTooltip
1367 // Tell the registered chrome that they should rollup the tooltip
1368 // NOTE: This routine is safe to call even if the popup is already closed.
1370 NS_IMETHODIMP
1371 ChromeTooltipListener::HideTooltip()
1373 nsresult rv = NS_OK;
1375 // shut down the relevant timers
1376 if ( mTooltipTimer ) {
1377 mTooltipTimer->Cancel();
1378 mTooltipTimer = nullptr;
1379 // release tooltip target
1380 mPossibleTooltipNode = nullptr;
1382 if ( mAutoHideTimer ) {
1383 mAutoHideTimer->Cancel();
1384 mAutoHideTimer = nullptr;
1387 // if we're showing the tip, tell the chrome to hide it
1388 if ( mShowingTooltip ) {
1389 nsCOMPtr<nsITooltipListener> tooltipListener ( do_QueryInterface(mWebBrowserChrome) );
1390 if ( tooltipListener ) {
1391 rv = tooltipListener->OnHideTooltip ( );
1392 if ( NS_SUCCEEDED(rv) )
1393 mShowingTooltip = false;
1397 return rv;
1399 } // HideTooltip
1403 // sTooltipCallback
1405 // A timer callback, fired when the mouse has hovered inside of a frame for the
1406 // appropriate amount of time. Getting to this point means that we should show the
1407 // tooltip, but only after we determine there is an appropriate TITLE element.
1409 // This relies on certain things being cached into the |aChromeTooltipListener| object passed to
1410 // us by the timer:
1411 // -- the x/y coordinates of the mouse (mMouseClientY, mMouseClientX)
1412 // -- the dom node the user hovered over (mPossibleTooltipNode)
1414 void
1415 ChromeTooltipListener::sTooltipCallback(nsITimer *aTimer,
1416 void *aChromeTooltipListener)
1418 ChromeTooltipListener* self = static_cast<ChromeTooltipListener*>
1419 (aChromeTooltipListener);
1420 if ( self && self->mPossibleTooltipNode ){
1421 // The actual coordinates we want to put the tooltip at are relative to the
1422 // toplevel docshell of our mWebBrowser. We know what the screen
1423 // coordinates of the mouse event were, which means we just need the screen
1424 // coordinates of the docshell. Unfortunately, there is no good way to
1425 // find those short of groveling for the presentation in that docshell and
1426 // finding the screen coords of its toplevel widget...
1427 nsCOMPtr<nsIDocShell> docShell =
1428 do_GetInterface(static_cast<nsIWebBrowser*>(self->mWebBrowser));
1429 nsCOMPtr<nsIPresShell> shell;
1430 if (docShell) {
1431 shell = docShell->GetPresShell();
1434 nsIWidget* widget = nullptr;
1435 if (shell) {
1436 nsViewManager* vm = shell->GetViewManager();
1437 if (vm) {
1438 nsView* view = vm->GetRootView();
1439 if (view) {
1440 nsPoint offset;
1441 widget = view->GetNearestWidget(&offset);
1446 if (!widget) {
1447 // release tooltip target if there is one, NO MATTER WHAT
1448 self->mPossibleTooltipNode = nullptr;
1449 return;
1452 // if there is text associated with the node, show the tip and fire
1453 // off a timer to auto-hide it.
1455 nsXPIDLString tooltipText;
1456 if (self->mTooltipTextProvider) {
1457 bool textFound = false;
1459 self->mTooltipTextProvider->GetNodeText(
1460 self->mPossibleTooltipNode, getter_Copies(tooltipText), &textFound);
1462 if (textFound) {
1463 nsString tipText(tooltipText);
1464 self->CreateAutoHideTimer();
1465 nsIntPoint screenDot = widget->WidgetToScreenOffset();
1466 self->ShowTooltip (self->mMouseScreenX - screenDot.x,
1467 self->mMouseScreenY - screenDot.y,
1468 tipText);
1472 // release tooltip target if there is one, NO MATTER WHAT
1473 self->mPossibleTooltipNode = nullptr;
1474 } // if "self" data valid
1476 } // sTooltipCallback
1480 // CreateAutoHideTimer
1482 // Create a new timer to see if we should auto-hide. It's ok if this fails.
1484 void
1485 ChromeTooltipListener::CreateAutoHideTimer()
1487 // just to be anal (er, safe)
1488 if ( mAutoHideTimer ) {
1489 mAutoHideTimer->Cancel();
1490 mAutoHideTimer = nullptr;
1493 mAutoHideTimer = do_CreateInstance("@mozilla.org/timer;1");
1494 if ( mAutoHideTimer )
1495 mAutoHideTimer->InitWithFuncCallback(sAutoHideCallback, this, kTooltipAutoHideTime,
1496 nsITimer::TYPE_ONE_SHOT);
1498 } // CreateAutoHideTimer
1502 // sAutoHideCallback
1504 // This fires after a tooltip has been open for a certain length of time. Just tell
1505 // the listener to close the popup. We don't have to worry, because HideTooltip() can
1506 // be called multiple times, even if the tip has already been closed.
1508 void
1509 ChromeTooltipListener::sAutoHideCallback(nsITimer *aTimer, void* aListener)
1511 ChromeTooltipListener* self = static_cast<ChromeTooltipListener*>(aListener);
1512 if ( self )
1513 self->HideTooltip();
1515 // NOTE: |aTimer| and |self->mAutoHideTimer| are invalid after calling ClosePopup();
1517 } // sAutoHideCallback
1520 NS_IMPL_ISUPPORTS(ChromeContextMenuListener, nsIDOMEventListener)
1524 // ChromeTooltipListener ctor
1526 ChromeContextMenuListener::ChromeContextMenuListener(nsWebBrowser* inBrowser, nsIWebBrowserChrome* inChrome )
1527 : mContextMenuListenerInstalled(false),
1528 mWebBrowser(inBrowser),
1529 mWebBrowserChrome(inChrome)
1531 } // ctor
1535 // ChromeTooltipListener dtor
1537 ChromeContextMenuListener::~ChromeContextMenuListener()
1539 } // dtor
1543 // AddContextMenuListener
1545 // Subscribe to the events that will allow us to track context menus. Bascially, this
1546 // is just the context-menu DOM event.
1548 NS_IMETHODIMP
1549 ChromeContextMenuListener::AddContextMenuListener()
1551 if (mEventTarget) {
1552 nsresult rv =
1553 mEventTarget->AddEventListener(NS_LITERAL_STRING("contextmenu"), this,
1554 false, false);
1555 NS_ENSURE_SUCCESS(rv, rv);
1557 mContextMenuListenerInstalled = true;
1560 return NS_OK;
1565 // RemoveContextMenuListener
1567 // Unsubscribe from all the various context menu events that we were listening to.
1569 NS_IMETHODIMP
1570 ChromeContextMenuListener::RemoveContextMenuListener()
1572 if (mEventTarget) {
1573 nsresult rv =
1574 mEventTarget->RemoveEventListener(NS_LITERAL_STRING("contextmenu"), this,
1575 false);
1576 NS_ENSURE_SUCCESS(rv, rv);
1578 mContextMenuListenerInstalled = false;
1581 return NS_OK;
1586 // AddChromeListeners
1588 // Hook up things to the chrome like context menus and tooltips, if the chrome
1589 // has implemented the right interfaces.
1591 NS_IMETHODIMP
1592 ChromeContextMenuListener::AddChromeListeners()
1594 if (!mEventTarget)
1595 GetDOMEventTarget(mWebBrowser, getter_AddRefs(mEventTarget));
1597 // Register the appropriate events for context menus, but only if
1598 // the embedding chrome cares.
1599 nsresult rv = NS_OK;
1601 nsCOMPtr<nsIContextMenuListener2> contextListener2 ( do_QueryInterface(mWebBrowserChrome) );
1602 nsCOMPtr<nsIContextMenuListener> contextListener ( do_QueryInterface(mWebBrowserChrome) );
1603 if ( (contextListener || contextListener2) && !mContextMenuListenerInstalled )
1604 rv = AddContextMenuListener();
1606 return rv;
1608 } // AddChromeListeners
1612 // RemoveChromeListeners
1614 // Unsubscribe from the various things we've hooked up to the window root.
1616 NS_IMETHODIMP
1617 ChromeContextMenuListener::RemoveChromeListeners()
1619 if ( mContextMenuListenerInstalled )
1620 RemoveContextMenuListener();
1622 mEventTarget = nullptr;
1624 // it really doesn't matter if these fail...
1625 return NS_OK;
1627 } // RemoveChromeTooltipListeners
1632 // ContextMenu
1634 // We're on call to show the context menu. Dig around in the DOM to
1635 // find the type of object we're dealing with and notify the front
1636 // end chrome.
1638 NS_IMETHODIMP
1639 ChromeContextMenuListener::HandleEvent(nsIDOMEvent* aMouseEvent)
1641 nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aMouseEvent);
1642 NS_ENSURE_TRUE(mouseEvent, NS_ERROR_UNEXPECTED);
1644 bool isDefaultPrevented = false;
1645 aMouseEvent->GetDefaultPrevented(&isDefaultPrevented);
1646 if (isDefaultPrevented) {
1647 return NS_OK;
1650 nsCOMPtr<EventTarget> targetNode = aMouseEvent->InternalDOMEvent()->GetTarget();
1651 if (!targetNode)
1652 return NS_ERROR_NULL_POINTER;
1654 nsCOMPtr<nsIDOMNode> targetDOMnode;
1655 nsCOMPtr<nsIDOMNode> node = do_QueryInterface(targetNode);
1656 if (!node)
1657 return NS_OK;
1659 // Stop the context menu event going to other windows (bug 78396)
1660 aMouseEvent->PreventDefault();
1662 // If the listener is a nsIContextMenuListener2, create the info object
1663 nsCOMPtr<nsIContextMenuListener2> menuListener2(do_QueryInterface(mWebBrowserChrome));
1664 nsContextMenuInfo *menuInfoImpl = nullptr;
1665 nsCOMPtr<nsIContextMenuInfo> menuInfo;
1666 if (menuListener2) {
1667 menuInfoImpl = new nsContextMenuInfo;
1668 menuInfo = menuInfoImpl;
1671 uint32_t flags = nsIContextMenuListener::CONTEXT_NONE;
1672 uint32_t flags2 = nsIContextMenuListener2::CONTEXT_NONE;
1674 // XXX test for selected text
1676 uint16_t nodeType;
1677 nsresult res = node->GetNodeType(&nodeType);
1678 NS_ENSURE_SUCCESS(res, res);
1680 // First, checks for nodes that never have children.
1681 if (nodeType == nsIDOMNode::ELEMENT_NODE) {
1682 nsCOMPtr<nsIImageLoadingContent> content(do_QueryInterface(node));
1683 if (content) {
1684 nsCOMPtr<nsIURI> imgUri;
1685 content->GetCurrentURI(getter_AddRefs(imgUri));
1686 if (imgUri) {
1687 flags |= nsIContextMenuListener::CONTEXT_IMAGE;
1688 flags2 |= nsIContextMenuListener2::CONTEXT_IMAGE;
1689 targetDOMnode = node;
1693 nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(node));
1694 if (formControl) {
1695 if (formControl->GetType() == NS_FORM_TEXTAREA) {
1696 flags |= nsIContextMenuListener::CONTEXT_TEXT;
1697 flags2 |= nsIContextMenuListener2::CONTEXT_TEXT;
1698 targetDOMnode = node;
1699 } else {
1700 nsCOMPtr<nsIDOMHTMLInputElement> inputElement(do_QueryInterface(formControl));
1701 if (inputElement) {
1702 flags |= nsIContextMenuListener::CONTEXT_INPUT;
1703 flags2 |= nsIContextMenuListener2::CONTEXT_INPUT;
1705 if (menuListener2) {
1706 if (formControl->IsSingleLineTextControl(false)) {
1707 flags2 |= nsIContextMenuListener2::CONTEXT_TEXT;
1711 targetDOMnode = node;
1716 // always consume events for plugins and Java who may throw their
1717 // own context menus but not for image objects. Document objects
1718 // will never be targets or ancestors of targets, so that's OK.
1719 nsCOMPtr<nsIDOMHTMLObjectElement> objectElement;
1720 if (!(flags & nsIContextMenuListener::CONTEXT_IMAGE))
1721 objectElement = do_QueryInterface(node);
1722 nsCOMPtr<nsIDOMHTMLEmbedElement> embedElement(do_QueryInterface(node));
1723 nsCOMPtr<nsIDOMHTMLAppletElement> appletElement(do_QueryInterface(node));
1725 if (objectElement || embedElement || appletElement)
1726 return NS_OK;
1729 // Bubble out, looking for items of interest
1730 do {
1731 uint16_t nodeType;
1732 res = node->GetNodeType(&nodeType);
1733 NS_ENSURE_SUCCESS(res, res);
1735 if (nodeType == nsIDOMNode::ELEMENT_NODE) {
1737 // Test if the element has an associated link
1738 nsCOMPtr<nsIDOMElement> element(do_QueryInterface(node));
1740 bool hasAttr = false;
1741 res = element->HasAttribute(NS_LITERAL_STRING("href"), &hasAttr);
1743 if (NS_SUCCEEDED(res) && hasAttr)
1745 flags |= nsIContextMenuListener::CONTEXT_LINK;
1746 flags2 |= nsIContextMenuListener2::CONTEXT_LINK;
1747 if (!targetDOMnode)
1748 targetDOMnode = node;
1749 if (menuInfoImpl)
1750 menuInfoImpl->SetAssociatedLink(node);
1751 break; // exit do-while
1755 // walk-up-the-tree
1756 nsCOMPtr<nsIDOMNode> parentNode;
1757 node->GetParentNode(getter_AddRefs(parentNode));
1758 node = parentNode;
1759 } while (node);
1761 if (!flags && !flags2) {
1762 // We found nothing of interest so far, check if we
1763 // have at least an html document.
1764 nsCOMPtr<nsIDOMDocument> document;
1765 node = do_QueryInterface(targetNode);
1766 node->GetOwnerDocument(getter_AddRefs(document));
1767 nsCOMPtr<nsIDOMHTMLDocument> htmlDocument(do_QueryInterface(document));
1768 if (htmlDocument) {
1769 flags |= nsIContextMenuListener::CONTEXT_DOCUMENT;
1770 flags2 |= nsIContextMenuListener2::CONTEXT_DOCUMENT;
1771 targetDOMnode = node;
1772 if (!(flags & nsIContextMenuListener::CONTEXT_IMAGE)) {
1773 // check if this is a background image that the user was trying to click on
1774 // and if the listener is ready for that (only nsIContextMenuListener2 and up)
1775 if (menuInfoImpl && menuInfoImpl->HasBackgroundImage(targetDOMnode)) {
1776 flags2 |= nsIContextMenuListener2::CONTEXT_BACKGROUND_IMAGE;
1777 // For the embedder to get the correct background image
1778 // targetDOMnode must point to the original node.
1779 targetDOMnode = do_QueryInterface(targetNode);
1785 // we need to cache the event target into the focus controller's popupNode
1786 // so we can get at it later from command code, etc.:
1788 // get the dom window
1789 nsCOMPtr<nsIDOMWindow> win;
1790 res = mWebBrowser->GetContentDOMWindow(getter_AddRefs(win));
1791 NS_ENSURE_SUCCESS(res, res);
1792 NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
1794 nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(win));
1795 NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
1796 nsCOMPtr<nsPIWindowRoot> root = window->GetTopWindowRoot();
1797 NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
1798 if (root) {
1799 // set the window root's popup node to the event target
1800 root->SetPopupNode(targetDOMnode);
1803 // Tell the listener all about the event
1804 if ( menuListener2 ) {
1805 menuInfoImpl->SetMouseEvent(aMouseEvent);
1806 menuInfoImpl->SetDOMNode(targetDOMnode);
1807 menuListener2->OnShowContextMenu(flags2, menuInfo);
1809 else {
1810 nsCOMPtr<nsIContextMenuListener> menuListener(do_QueryInterface(mWebBrowserChrome));
1811 if ( menuListener )
1812 menuListener->OnShowContextMenu(flags, aMouseEvent, targetDOMnode);
1815 return NS_OK;
1817 } // MouseDown