1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 // Local Includes
8 #include "nsDocShellTreeOwner.h"
9 #include "nsWebBrowser.h"
10
11 // Helper Classes
12 #include "nsContentUtils.h"
13 #include "nsSize.h"
14 #include "mozilla/ReflowInput.h"
15 #include "nsComponentManagerUtils.h"
16 #include "nsString.h"
17 #include "nsAtom.h"
18 #include "nsReadableUtils.h"
19 #include "nsUnicharUtils.h"
20 #include "mozilla/LookAndFeel.h"
21
22 // Interfaces needed to be included
23 #include "nsPresContext.h"
24 #include "nsITooltipListener.h"
25 #include "nsINode.h"
26 #include "Link.h"
27 #include "mozilla/dom/Element.h"
28 #include "mozilla/dom/MouseEvent.h"
29 #include "mozilla/dom/SVGTitleElement.h"
30 #include "nsIFormControl.h"
31 #include "nsIWebNavigation.h"
32 #include "nsPIDOMWindow.h"
33 #include "nsPIWindowRoot.h"
34 #include "nsIWindowWatcher.h"
35 #include "nsPIWindowWatcher.h"
36 #include "nsIPrompt.h"
37 #include "nsIRemoteTab.h"
38 #include "nsIBrowserChild.h"
39 #include "nsRect.h"
40 #include "nsIWebBrowserChromeFocus.h"
41 #include "nsIContent.h"
42 #include "nsViewManager.h"
43 #include "nsView.h"
44 #include "nsIConstraintValidation.h"
45 #include "mozilla/Attributes.h"
46 #include "mozilla/EventListenerManager.h"
47 #include "mozilla/dom/DragEvent.h"
48 #include "mozilla/dom/Event.h" // for Event
49 #include "mozilla/dom/File.h" // for input type=file
50 #include "mozilla/dom/FileList.h" // for input type=file
51 #include "mozilla/dom/LoadURIOptionsBinding.h"
52 #include "mozilla/PresShell.h"
53 #include "mozilla/TextEvents.h"
54
55 using namespace mozilla;
56 using namespace mozilla::dom;
57
58 // A helper routine that navigates the tricky path from a |nsWebBrowser| to
59 // a |EventTarget| via the window root and chrome event handler.
GetDOMEventTarget(nsWebBrowser * aInBrowser,EventTarget ** aTarget)60 static nsresult GetDOMEventTarget(nsWebBrowser* aInBrowser,
61 EventTarget** aTarget) {
62 if (!aInBrowser) {
63 return NS_ERROR_INVALID_POINTER;
64 }
65
66 nsCOMPtr<mozIDOMWindowProxy> domWindow;
67 aInBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
68 if (!domWindow) {
69 return NS_ERROR_FAILURE;
70 }
71
72 auto* outerWindow = nsPIDOMWindowOuter::From(domWindow);
73 nsPIDOMWindowOuter* rootWindow = outerWindow->GetPrivateRoot();
74 NS_ENSURE_TRUE(rootWindow, NS_ERROR_FAILURE);
75 nsCOMPtr<EventTarget> target = rootWindow->GetChromeEventHandler();
76 NS_ENSURE_TRUE(target, NS_ERROR_FAILURE);
77 target.forget(aTarget);
78
79 return NS_OK;
80 }
81
nsDocShellTreeOwner()82 nsDocShellTreeOwner::nsDocShellTreeOwner()
83 : mWebBrowser(nullptr),
84 mTreeOwner(nullptr),
85 mPrimaryContentShell(nullptr),
86 mWebBrowserChrome(nullptr),
87 mOwnerWin(nullptr),
88 mOwnerRequestor(nullptr) {}
89
~nsDocShellTreeOwner()90 nsDocShellTreeOwner::~nsDocShellTreeOwner() { RemoveChromeListeners(); }
91
92 NS_IMPL_ADDREF(nsDocShellTreeOwner)
NS_IMPL_RELEASE(nsDocShellTreeOwner)93 NS_IMPL_RELEASE(nsDocShellTreeOwner)
94
95 NS_INTERFACE_MAP_BEGIN(nsDocShellTreeOwner)
96 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocShellTreeOwner)
97 NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeOwner)
98 NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
99 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
100 NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
101 NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
102 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
103 NS_INTERFACE_MAP_END
104
105 //*****************************************************************************
106 // nsDocShellTreeOwner::nsIInterfaceRequestor
107 //*****************************************************************************
108
109 NS_IMETHODIMP
110 nsDocShellTreeOwner::GetInterface(const nsIID& aIID, void** aSink) {
111 NS_ENSURE_ARG_POINTER(aSink);
112
113 if (NS_SUCCEEDED(QueryInterface(aIID, aSink))) {
114 return NS_OK;
115 }
116
117 if (aIID.Equals(NS_GET_IID(nsIWebBrowserChromeFocus))) {
118 if (mWebBrowserChromeWeak != nullptr) {
119 return mWebBrowserChromeWeak->QueryReferent(aIID, aSink);
120 }
121 return mOwnerWin->QueryInterface(aIID, aSink);
122 }
123
124 if (aIID.Equals(NS_GET_IID(nsIPrompt))) {
125 nsCOMPtr<nsIPrompt> prompt;
126 EnsurePrompter();
127 prompt = mPrompter;
128 if (prompt) {
129 prompt.forget(aSink);
130 return NS_OK;
131 }
132 return NS_NOINTERFACE;
133 }
134
135 if (aIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
136 nsCOMPtr<nsIAuthPrompt> prompt;
137 EnsureAuthPrompter();
138 prompt = mAuthPrompter;
139 if (prompt) {
140 prompt.forget(aSink);
141 return NS_OK;
142 }
143 return NS_NOINTERFACE;
144 }
145
146 nsCOMPtr<nsIInterfaceRequestor> req = GetOwnerRequestor();
147 if (req) {
148 return req->GetInterface(aIID, aSink);
149 }
150
151 return NS_NOINTERFACE;
152 }
153
154 //*****************************************************************************
155 // nsDocShellTreeOwner::nsIDocShellTreeOwner
156 //*****************************************************************************
157
EnsurePrompter()158 void nsDocShellTreeOwner::EnsurePrompter() {
159 if (mPrompter) {
160 return;
161 }
162
163 nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
164 if (wwatch && mWebBrowser) {
165 nsCOMPtr<mozIDOMWindowProxy> domWindow;
166 mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
167 if (domWindow) {
168 wwatch->GetNewPrompter(domWindow, getter_AddRefs(mPrompter));
169 }
170 }
171 }
172
EnsureAuthPrompter()173 void nsDocShellTreeOwner::EnsureAuthPrompter() {
174 if (mAuthPrompter) {
175 return;
176 }
177
178 nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
179 if (wwatch && mWebBrowser) {
180 nsCOMPtr<mozIDOMWindowProxy> domWindow;
181 mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
182 if (domWindow) {
183 wwatch->GetNewAuthPrompter(domWindow, getter_AddRefs(mAuthPrompter));
184 }
185 }
186 }
187
AddToWatcher()188 void nsDocShellTreeOwner::AddToWatcher() {
189 if (mWebBrowser) {
190 nsCOMPtr<mozIDOMWindowProxy> domWindow;
191 mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
192 if (domWindow) {
193 nsCOMPtr<nsPIWindowWatcher> wwatch(
194 do_GetService(NS_WINDOWWATCHER_CONTRACTID));
195 if (wwatch) {
196 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
197 if (webBrowserChrome) {
198 wwatch->AddWindow(domWindow, webBrowserChrome);
199 }
200 }
201 }
202 }
203 }
204
RemoveFromWatcher()205 void nsDocShellTreeOwner::RemoveFromWatcher() {
206 if (mWebBrowser) {
207 nsCOMPtr<mozIDOMWindowProxy> domWindow;
208 mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
209 if (domWindow) {
210 nsCOMPtr<nsPIWindowWatcher> wwatch(
211 do_GetService(NS_WINDOWWATCHER_CONTRACTID));
212 if (wwatch) {
213 wwatch->RemoveWindow(domWindow);
214 }
215 }
216 }
217 }
218
EnsureContentTreeOwner()219 void nsDocShellTreeOwner::EnsureContentTreeOwner() {
220 if (mContentTreeOwner) {
221 return;
222 }
223
224 mContentTreeOwner = new nsDocShellTreeOwner();
225 nsCOMPtr<nsIWebBrowserChrome> browserChrome = GetWebBrowserChrome();
226 if (browserChrome) {
227 mContentTreeOwner->SetWebBrowserChrome(browserChrome);
228 }
229
230 if (mWebBrowser) {
231 mContentTreeOwner->WebBrowser(mWebBrowser);
232 }
233 }
234
235 NS_IMETHODIMP
ContentShellAdded(nsIDocShellTreeItem * aContentShell,bool aPrimary)236 nsDocShellTreeOwner::ContentShellAdded(nsIDocShellTreeItem* aContentShell,
237 bool aPrimary) {
238 if (mTreeOwner) return mTreeOwner->ContentShellAdded(aContentShell, aPrimary);
239
240 EnsureContentTreeOwner();
241 aContentShell->SetTreeOwner(mContentTreeOwner);
242
243 if (aPrimary) {
244 mPrimaryContentShell = aContentShell;
245 mPrimaryRemoteTab = nullptr;
246 }
247 return NS_OK;
248 }
249
250 NS_IMETHODIMP
ContentShellRemoved(nsIDocShellTreeItem * aContentShell)251 nsDocShellTreeOwner::ContentShellRemoved(nsIDocShellTreeItem* aContentShell) {
252 if (mTreeOwner) {
253 return mTreeOwner->ContentShellRemoved(aContentShell);
254 }
255
256 if (mPrimaryContentShell == aContentShell) {
257 mPrimaryContentShell = nullptr;
258 }
259
260 return NS_OK;
261 }
262
263 NS_IMETHODIMP
GetPrimaryContentShell(nsIDocShellTreeItem ** aShell)264 nsDocShellTreeOwner::GetPrimaryContentShell(nsIDocShellTreeItem** aShell) {
265 NS_ENSURE_ARG_POINTER(aShell);
266
267 if (mTreeOwner) {
268 return mTreeOwner->GetPrimaryContentShell(aShell);
269 }
270
271 nsCOMPtr<nsIDocShellTreeItem> shell;
272 if (!mPrimaryRemoteTab) {
273 shell =
274 mPrimaryContentShell ? mPrimaryContentShell : mWebBrowser->mDocShell;
275 }
276 shell.forget(aShell);
277
278 return NS_OK;
279 }
280
281 NS_IMETHODIMP
RemoteTabAdded(nsIRemoteTab * aTab,bool aPrimary)282 nsDocShellTreeOwner::RemoteTabAdded(nsIRemoteTab* aTab, bool aPrimary) {
283 if (mTreeOwner) {
284 return mTreeOwner->RemoteTabAdded(aTab, aPrimary);
285 }
286
287 if (aPrimary) {
288 mPrimaryRemoteTab = aTab;
289 mPrimaryContentShell = nullptr;
290 } else if (mPrimaryRemoteTab == aTab) {
291 mPrimaryRemoteTab = nullptr;
292 }
293
294 return NS_OK;
295 }
296
297 NS_IMETHODIMP
RemoteTabRemoved(nsIRemoteTab * aTab)298 nsDocShellTreeOwner::RemoteTabRemoved(nsIRemoteTab* aTab) {
299 if (mTreeOwner) {
300 return mTreeOwner->RemoteTabRemoved(aTab);
301 }
302
303 if (aTab == mPrimaryRemoteTab) {
304 mPrimaryRemoteTab = nullptr;
305 }
306
307 return NS_OK;
308 }
309
310 NS_IMETHODIMP
GetPrimaryRemoteTab(nsIRemoteTab ** aTab)311 nsDocShellTreeOwner::GetPrimaryRemoteTab(nsIRemoteTab** aTab) {
312 if (mTreeOwner) {
313 return mTreeOwner->GetPrimaryRemoteTab(aTab);
314 }
315
316 nsCOMPtr<nsIRemoteTab> tab = mPrimaryRemoteTab;
317 tab.forget(aTab);
318 return NS_OK;
319 }
320
321 NS_IMETHODIMP
GetPrimaryContentSize(int32_t * aWidth,int32_t * aHeight)322 nsDocShellTreeOwner::GetPrimaryContentSize(int32_t* aWidth, int32_t* aHeight) {
323 return NS_ERROR_NOT_IMPLEMENTED;
324 }
325
326 NS_IMETHODIMP
SetPrimaryContentSize(int32_t aWidth,int32_t aHeight)327 nsDocShellTreeOwner::SetPrimaryContentSize(int32_t aWidth, int32_t aHeight) {
328 return NS_ERROR_NOT_IMPLEMENTED;
329 }
330
331 NS_IMETHODIMP
GetRootShellSize(int32_t * aWidth,int32_t * aHeight)332 nsDocShellTreeOwner::GetRootShellSize(int32_t* aWidth, int32_t* aHeight) {
333 return NS_ERROR_NOT_IMPLEMENTED;
334 }
335
336 NS_IMETHODIMP
SetRootShellSize(int32_t aWidth,int32_t aHeight)337 nsDocShellTreeOwner::SetRootShellSize(int32_t aWidth, int32_t aHeight) {
338 return NS_ERROR_NOT_IMPLEMENTED;
339 }
340
341 NS_IMETHODIMP
SizeShellTo(nsIDocShellTreeItem * aShellItem,int32_t aCX,int32_t aCY)342 nsDocShellTreeOwner::SizeShellTo(nsIDocShellTreeItem* aShellItem, int32_t aCX,
343 int32_t aCY) {
344 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
345
346 NS_ENSURE_STATE(mTreeOwner || webBrowserChrome);
347
348 if (nsCOMPtr<nsIDocShellTreeOwner> treeOwner = mTreeOwner) {
349 return treeOwner->SizeShellTo(aShellItem, aCX, aCY);
350 }
351
352 if (aShellItem == mWebBrowser->mDocShell) {
353 nsCOMPtr<nsIBrowserChild> browserChild =
354 do_QueryInterface(webBrowserChrome);
355 if (browserChild) {
356 // The XUL window to resize is in the parent process, but there we
357 // won't be able to get aShellItem to do the hack in
358 // AppWindow::SizeShellTo, so let's send the width and height of
359 // aShellItem too.
360 nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(aShellItem));
361 NS_ENSURE_TRUE(shellAsWin, NS_ERROR_FAILURE);
362
363 int32_t width = 0;
364 int32_t height = 0;
365 shellAsWin->GetSize(&width, &height);
366 return browserChild->RemoteSizeShellTo(aCX, aCY, width, height);
367 }
368 // XXX: this is weird, but we used to call a method here
369 // (webBrowserChrome->SizeBrowserTo()) whose implementations all failed
370 // like this, so...
371 return NS_ERROR_NOT_IMPLEMENTED;
372 }
373
374 NS_ENSURE_TRUE(aShellItem, NS_ERROR_FAILURE);
375
376 RefPtr<Document> document = aShellItem->GetDocument();
377 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
378
379 NS_ENSURE_TRUE(document->GetDocumentElement(), NS_ERROR_FAILURE);
380
381 // Set the preferred Size
382 // XXX
383 NS_ERROR("Implement this");
384 /*
385 Set the preferred size on the aShellItem.
386 */
387
388 RefPtr<nsPresContext> presContext = mWebBrowser->mDocShell->GetPresContext();
389 NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
390
391 RefPtr<PresShell> presShell = presContext->GetPresShell();
392 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
393
394 NS_ENSURE_SUCCESS(
395 presShell->ResizeReflow(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE),
396 NS_ERROR_FAILURE);
397
398 // XXX: this is weird, but we used to call a method here
399 // (webBrowserChrome->SizeBrowserTo()) whose implementations all failed like
400 // this, so...
401 return NS_ERROR_NOT_IMPLEMENTED;
402 }
403
404 NS_IMETHODIMP
SetPersistence(bool aPersistPosition,bool aPersistSize,bool aPersistSizeMode)405 nsDocShellTreeOwner::SetPersistence(bool aPersistPosition, bool aPersistSize,
406 bool aPersistSizeMode) {
407 return NS_ERROR_NOT_IMPLEMENTED;
408 }
409
410 NS_IMETHODIMP
GetPersistence(bool * aPersistPosition,bool * aPersistSize,bool * aPersistSizeMode)411 nsDocShellTreeOwner::GetPersistence(bool* aPersistPosition, bool* aPersistSize,
412 bool* aPersistSizeMode) {
413 return NS_ERROR_NOT_IMPLEMENTED;
414 }
415
416 NS_IMETHODIMP
GetTabCount(uint32_t * aResult)417 nsDocShellTreeOwner::GetTabCount(uint32_t* aResult) {
418 if (mTreeOwner) {
419 return mTreeOwner->GetTabCount(aResult);
420 }
421
422 *aResult = 0;
423 return NS_OK;
424 }
425
426 NS_IMETHODIMP
GetHasPrimaryContent(bool * aResult)427 nsDocShellTreeOwner::GetHasPrimaryContent(bool* aResult) {
428 *aResult = mPrimaryRemoteTab || mPrimaryContentShell;
429 return NS_OK;
430 }
431
432 //*****************************************************************************
433 // nsDocShellTreeOwner::nsIBaseWindow
434 //*****************************************************************************
435
436 NS_IMETHODIMP
InitWindow(nativeWindow aParentNativeWindow,nsIWidget * aParentWidget,int32_t aX,int32_t aY,int32_t aCX,int32_t aCY)437 nsDocShellTreeOwner::InitWindow(nativeWindow aParentNativeWindow,
438 nsIWidget* aParentWidget, int32_t aX,
439 int32_t aY, int32_t aCX, int32_t aCY) {
440 return NS_ERROR_NULL_POINTER;
441 }
442
443 NS_IMETHODIMP
Create()444 nsDocShellTreeOwner::Create() { return NS_ERROR_NULL_POINTER; }
445
446 NS_IMETHODIMP
Destroy()447 nsDocShellTreeOwner::Destroy() {
448 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
449 if (webBrowserChrome) {
450 // XXX: this is weird, but we used to call a method here
451 // (webBrowserChrome->DestroyBrowserWindow()) whose implementations all
452 // failed like this, so...
453 return NS_ERROR_NOT_IMPLEMENTED;
454 }
455
456 return NS_ERROR_NULL_POINTER;
457 }
458
459 NS_IMETHODIMP
GetUnscaledDevicePixelsPerCSSPixel(double * aScale)460 nsDocShellTreeOwner::GetUnscaledDevicePixelsPerCSSPixel(double* aScale) {
461 if (mWebBrowser) {
462 return mWebBrowser->GetUnscaledDevicePixelsPerCSSPixel(aScale);
463 }
464
465 *aScale = 1.0;
466 return NS_OK;
467 }
468
469 NS_IMETHODIMP
GetDevicePixelsPerDesktopPixel(double * aScale)470 nsDocShellTreeOwner::GetDevicePixelsPerDesktopPixel(double* aScale) {
471 if (mWebBrowser) {
472 return mWebBrowser->GetDevicePixelsPerDesktopPixel(aScale);
473 }
474
475 *aScale = 1.0;
476 return NS_OK;
477 }
478
479 NS_IMETHODIMP
SetPositionDesktopPix(int32_t aX,int32_t aY)480 nsDocShellTreeOwner::SetPositionDesktopPix(int32_t aX, int32_t aY) {
481 if (mWebBrowser) {
482 nsresult rv = mWebBrowser->SetPositionDesktopPix(aX, aY);
483 NS_ENSURE_SUCCESS(rv, rv);
484 }
485
486 double scale = 1.0;
487 GetDevicePixelsPerDesktopPixel(&scale);
488 return SetPosition(NSToIntRound(aX * scale), NSToIntRound(aY * scale));
489 }
490
491 NS_IMETHODIMP
SetPosition(int32_t aX,int32_t aY)492 nsDocShellTreeOwner::SetPosition(int32_t aX, int32_t aY) {
493 nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
494 if (ownerWin) {
495 return ownerWin->SetDimensions(nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION,
496 aX, aY, 0, 0);
497 }
498 return NS_ERROR_NULL_POINTER;
499 }
500
501 NS_IMETHODIMP
GetPosition(int32_t * aX,int32_t * aY)502 nsDocShellTreeOwner::GetPosition(int32_t* aX, int32_t* aY) {
503 nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
504 if (ownerWin) {
505 return ownerWin->GetDimensions(nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION,
506 aX, aY, nullptr, nullptr);
507 }
508 return NS_ERROR_NULL_POINTER;
509 }
510
511 NS_IMETHODIMP
SetSize(int32_t aCX,int32_t aCY,bool aRepaint)512 nsDocShellTreeOwner::SetSize(int32_t aCX, int32_t aCY, bool aRepaint) {
513 nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
514 if (ownerWin) {
515 return ownerWin->SetDimensions(nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_OUTER,
516 0, 0, aCX, aCY);
517 }
518 return NS_ERROR_NULL_POINTER;
519 }
520
521 NS_IMETHODIMP
GetSize(int32_t * aCX,int32_t * aCY)522 nsDocShellTreeOwner::GetSize(int32_t* aCX, int32_t* aCY) {
523 nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
524 if (ownerWin) {
525 return ownerWin->GetDimensions(nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_OUTER,
526 nullptr, nullptr, aCX, aCY);
527 }
528 return NS_ERROR_NULL_POINTER;
529 }
530
531 NS_IMETHODIMP
SetPositionAndSize(int32_t aX,int32_t aY,int32_t aCX,int32_t aCY,uint32_t aFlags)532 nsDocShellTreeOwner::SetPositionAndSize(int32_t aX, int32_t aY, int32_t aCX,
533 int32_t aCY, uint32_t aFlags) {
534 nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
535 if (ownerWin) {
536 return ownerWin->SetDimensions(
537 nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_OUTER |
538 nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION,
539 aX, aY, aCX, aCY);
540 }
541 return NS_ERROR_NULL_POINTER;
542 }
543
544 NS_IMETHODIMP
GetPositionAndSize(int32_t * aX,int32_t * aY,int32_t * aCX,int32_t * aCY)545 nsDocShellTreeOwner::GetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aCX,
546 int32_t* aCY) {
547 nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
548 if (ownerWin) {
549 return ownerWin->GetDimensions(
550 nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_OUTER |
551 nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION,
552 aX, aY, aCX, aCY);
553 }
554 return NS_ERROR_NULL_POINTER;
555 }
556
557 NS_IMETHODIMP
Repaint(bool aForce)558 nsDocShellTreeOwner::Repaint(bool aForce) { return NS_ERROR_NULL_POINTER; }
559
560 NS_IMETHODIMP
GetParentWidget(nsIWidget ** aParentWidget)561 nsDocShellTreeOwner::GetParentWidget(nsIWidget** aParentWidget) {
562 return NS_ERROR_NULL_POINTER;
563 }
564
565 NS_IMETHODIMP
SetParentWidget(nsIWidget * aParentWidget)566 nsDocShellTreeOwner::SetParentWidget(nsIWidget* aParentWidget) {
567 return NS_ERROR_NULL_POINTER;
568 }
569
570 NS_IMETHODIMP
GetParentNativeWindow(nativeWindow * aParentNativeWindow)571 nsDocShellTreeOwner::GetParentNativeWindow(nativeWindow* aParentNativeWindow) {
572 nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
573 if (ownerWin) {
574 return ownerWin->GetSiteWindow(aParentNativeWindow);
575 }
576 return NS_ERROR_NULL_POINTER;
577 }
578
579 NS_IMETHODIMP
SetParentNativeWindow(nativeWindow aParentNativeWindow)580 nsDocShellTreeOwner::SetParentNativeWindow(nativeWindow aParentNativeWindow) {
581 return NS_ERROR_NULL_POINTER;
582 }
583
584 NS_IMETHODIMP
GetNativeHandle(nsAString & aNativeHandle)585 nsDocShellTreeOwner::GetNativeHandle(nsAString& aNativeHandle) {
586 // the nativeHandle should be accessed from nsIAppWindow
587 return NS_ERROR_NOT_IMPLEMENTED;
588 }
589
590 NS_IMETHODIMP
GetVisibility(bool * aVisibility)591 nsDocShellTreeOwner::GetVisibility(bool* aVisibility) {
592 nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
593 if (ownerWin) {
594 return ownerWin->GetVisibility(aVisibility);
595 }
596 return NS_ERROR_NULL_POINTER;
597 }
598
599 NS_IMETHODIMP
SetVisibility(bool aVisibility)600 nsDocShellTreeOwner::SetVisibility(bool aVisibility) {
601 nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
602 if (ownerWin) {
603 return ownerWin->SetVisibility(aVisibility);
604 }
605 return NS_ERROR_NULL_POINTER;
606 }
607
608 NS_IMETHODIMP
GetEnabled(bool * aEnabled)609 nsDocShellTreeOwner::GetEnabled(bool* aEnabled) {
610 NS_ENSURE_ARG_POINTER(aEnabled);
611 *aEnabled = true;
612 return NS_ERROR_NOT_IMPLEMENTED;
613 }
614
615 NS_IMETHODIMP
SetEnabled(bool aEnabled)616 nsDocShellTreeOwner::SetEnabled(bool aEnabled) {
617 return NS_ERROR_NOT_IMPLEMENTED;
618 }
619
620 NS_IMETHODIMP
GetMainWidget(nsIWidget ** aMainWidget)621 nsDocShellTreeOwner::GetMainWidget(nsIWidget** aMainWidget) {
622 return NS_ERROR_NULL_POINTER;
623 }
624
625 NS_IMETHODIMP
SetFocus()626 nsDocShellTreeOwner::SetFocus() {
627 nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
628 if (ownerWin) {
629 return ownerWin->SetFocus();
630 }
631 return NS_ERROR_NULL_POINTER;
632 }
633
634 NS_IMETHODIMP
GetTitle(nsAString & aTitle)635 nsDocShellTreeOwner::GetTitle(nsAString& aTitle) {
636 nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
637 if (ownerWin) {
638 return ownerWin->GetTitle(aTitle);
639 }
640 return NS_ERROR_NULL_POINTER;
641 }
642
643 NS_IMETHODIMP
SetTitle(const nsAString & aTitle)644 nsDocShellTreeOwner::SetTitle(const nsAString& aTitle) {
645 nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
646 if (ownerWin) {
647 return ownerWin->SetTitle(aTitle);
648 }
649 return NS_ERROR_NULL_POINTER;
650 }
651
652 //*****************************************************************************
653 // nsDocShellTreeOwner::nsIWebProgressListener
654 //*****************************************************************************
655
656 NS_IMETHODIMP
OnProgressChange(nsIWebProgress * aProgress,nsIRequest * aRequest,int32_t aCurSelfProgress,int32_t aMaxSelfProgress,int32_t aCurTotalProgress,int32_t aMaxTotalProgress)657 nsDocShellTreeOwner::OnProgressChange(nsIWebProgress* aProgress,
658 nsIRequest* aRequest,
659 int32_t aCurSelfProgress,
660 int32_t aMaxSelfProgress,
661 int32_t aCurTotalProgress,
662 int32_t aMaxTotalProgress) {
663 // In the absence of DOM document creation event, this method is the
664 // most convenient place to install the mouse listener on the
665 // DOM document.
666 return AddChromeListeners();
667 }
668
669 NS_IMETHODIMP
OnStateChange(nsIWebProgress * aProgress,nsIRequest * aRequest,uint32_t aProgressStateFlags,nsresult aStatus)670 nsDocShellTreeOwner::OnStateChange(nsIWebProgress* aProgress,
671 nsIRequest* aRequest,
672 uint32_t aProgressStateFlags,
673 nsresult aStatus) {
674 return NS_OK;
675 }
676
677 NS_IMETHODIMP
OnLocationChange(nsIWebProgress * aWebProgress,nsIRequest * aRequest,nsIURI * aURI,uint32_t aFlags)678 nsDocShellTreeOwner::OnLocationChange(nsIWebProgress* aWebProgress,
679 nsIRequest* aRequest, nsIURI* aURI,
680 uint32_t aFlags) {
681 if (mChromeTooltipListener && aWebProgress &&
682 !(aFlags & nsIWebProgressListener::LOCATION_CHANGE_SAME_DOCUMENT) &&
683 mChromeTooltipListener->WebProgressShowedTooltip(aWebProgress)) {
684 mChromeTooltipListener->HideTooltip();
685 }
686 return NS_OK;
687 }
688
689 NS_IMETHODIMP
OnStatusChange(nsIWebProgress * aWebProgress,nsIRequest * aRequest,nsresult aStatus,const char16_t * aMessage)690 nsDocShellTreeOwner::OnStatusChange(nsIWebProgress* aWebProgress,
691 nsIRequest* aRequest, nsresult aStatus,
692 const char16_t* aMessage) {
693 return NS_OK;
694 }
695
696 NS_IMETHODIMP
OnSecurityChange(nsIWebProgress * aWebProgress,nsIRequest * aRequest,uint32_t aState)697 nsDocShellTreeOwner::OnSecurityChange(nsIWebProgress* aWebProgress,
698 nsIRequest* aRequest, uint32_t aState) {
699 return NS_OK;
700 }
701
702 NS_IMETHODIMP
OnContentBlockingEvent(nsIWebProgress * aWebProgress,nsIRequest * aRequest,uint32_t aEvent)703 nsDocShellTreeOwner::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
704 nsIRequest* aRequest,
705 uint32_t aEvent) {
706 return NS_OK;
707 }
708
709 //*****************************************************************************
710 // nsDocShellTreeOwner: Accessors
711 //*****************************************************************************
712
WebBrowser(nsWebBrowser * aWebBrowser)713 void nsDocShellTreeOwner::WebBrowser(nsWebBrowser* aWebBrowser) {
714 if (!aWebBrowser) {
715 RemoveChromeListeners();
716 }
717 if (aWebBrowser != mWebBrowser) {
718 mPrompter = nullptr;
719 mAuthPrompter = nullptr;
720 }
721
722 mWebBrowser = aWebBrowser;
723
724 if (mContentTreeOwner) {
725 mContentTreeOwner->WebBrowser(aWebBrowser);
726 if (!aWebBrowser) {
727 mContentTreeOwner = nullptr;
728 }
729 }
730 }
731
WebBrowser()732 nsWebBrowser* nsDocShellTreeOwner::WebBrowser() { return mWebBrowser; }
733
734 NS_IMETHODIMP
SetTreeOwner(nsIDocShellTreeOwner * aTreeOwner)735 nsDocShellTreeOwner::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner) {
736 if (aTreeOwner) {
737 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome(do_GetInterface(aTreeOwner));
738 NS_ENSURE_TRUE(webBrowserChrome, NS_ERROR_INVALID_ARG);
739 NS_ENSURE_SUCCESS(SetWebBrowserChrome(webBrowserChrome),
740 NS_ERROR_INVALID_ARG);
741 mTreeOwner = aTreeOwner;
742 } else {
743 mTreeOwner = nullptr;
744 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
745 if (!webBrowserChrome) {
746 NS_ENSURE_SUCCESS(SetWebBrowserChrome(nullptr), NS_ERROR_FAILURE);
747 }
748 }
749
750 return NS_OK;
751 }
752
753 NS_IMETHODIMP
SetWebBrowserChrome(nsIWebBrowserChrome * aWebBrowserChrome)754 nsDocShellTreeOwner::SetWebBrowserChrome(
755 nsIWebBrowserChrome* aWebBrowserChrome) {
756 if (!aWebBrowserChrome) {
757 mWebBrowserChrome = nullptr;
758 mOwnerWin = nullptr;
759 mOwnerRequestor = nullptr;
760 mWebBrowserChromeWeak = nullptr;
761 } else {
762 nsCOMPtr<nsISupportsWeakReference> supportsweak =
763 do_QueryInterface(aWebBrowserChrome);
764 if (supportsweak) {
765 supportsweak->GetWeakReference(getter_AddRefs(mWebBrowserChromeWeak));
766 } else {
767 nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin(
768 do_QueryInterface(aWebBrowserChrome));
769 nsCOMPtr<nsIInterfaceRequestor> requestor(
770 do_QueryInterface(aWebBrowserChrome));
771
772 // it's ok for ownerWin or requestor to be null.
773 mWebBrowserChrome = aWebBrowserChrome;
774 mOwnerWin = ownerWin;
775 mOwnerRequestor = requestor;
776 }
777 }
778
779 if (mContentTreeOwner) {
780 mContentTreeOwner->SetWebBrowserChrome(aWebBrowserChrome);
781 }
782
783 return NS_OK;
784 }
785
786 // Hook up things to the chrome like context menus and tooltips, if the chrome
787 // has implemented the right interfaces.
788 NS_IMETHODIMP
AddChromeListeners()789 nsDocShellTreeOwner::AddChromeListeners() {
790 nsresult rv = NS_OK;
791
792 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
793 if (!webBrowserChrome) {
794 return NS_ERROR_FAILURE;
795 }
796
797 // install tooltips
798 if (!mChromeTooltipListener) {
799 nsCOMPtr<nsITooltipListener> tooltipListener(
800 do_QueryInterface(webBrowserChrome));
801 if (tooltipListener) {
802 mChromeTooltipListener =
803 new ChromeTooltipListener(mWebBrowser, webBrowserChrome);
804 rv = mChromeTooltipListener->AddChromeListeners();
805 }
806 }
807
808 // register dragover and drop event listeners with the listener manager
809 nsCOMPtr<EventTarget> target;
810 GetDOMEventTarget(mWebBrowser, getter_AddRefs(target));
811
812 EventListenerManager* elmP = target->GetOrCreateListenerManager();
813 if (elmP) {
814 elmP->AddEventListenerByType(this, NS_LITERAL_STRING("dragover"),
815 TrustedEventsAtSystemGroupBubble());
816 elmP->AddEventListenerByType(this, NS_LITERAL_STRING("drop"),
817 TrustedEventsAtSystemGroupBubble());
818 }
819
820 return rv;
821 }
822
823 NS_IMETHODIMP
RemoveChromeListeners()824 nsDocShellTreeOwner::RemoveChromeListeners() {
825 if (mChromeTooltipListener) {
826 mChromeTooltipListener->RemoveChromeListeners();
827 mChromeTooltipListener = nullptr;
828 }
829
830 nsCOMPtr<EventTarget> piTarget;
831 GetDOMEventTarget(mWebBrowser, getter_AddRefs(piTarget));
832 if (!piTarget) {
833 return NS_OK;
834 }
835
836 EventListenerManager* elmP = piTarget->GetOrCreateListenerManager();
837 if (elmP) {
838 elmP->RemoveEventListenerByType(this, NS_LITERAL_STRING("dragover"),
839 TrustedEventsAtSystemGroupBubble());
840 elmP->RemoveEventListenerByType(this, NS_LITERAL_STRING("drop"),
841 TrustedEventsAtSystemGroupBubble());
842 }
843
844 return NS_OK;
845 }
846
847 NS_IMETHODIMP
HandleEvent(Event * aEvent)848 nsDocShellTreeOwner::HandleEvent(Event* aEvent) {
849 DragEvent* dragEvent = aEvent ? aEvent->AsDragEvent() : nullptr;
850 if (NS_WARN_IF(!dragEvent)) {
851 return NS_ERROR_INVALID_ARG;
852 }
853
854 if (dragEvent->DefaultPrevented()) {
855 return NS_OK;
856 }
857
858 nsCOMPtr<nsIDroppedLinkHandler> handler =
859 do_GetService("@mozilla.org/content/dropped-link-handler;1");
860 if (!handler) {
861 return NS_OK;
862 }
863
864 nsAutoString eventType;
865 aEvent->GetType(eventType);
866 if (eventType.EqualsLiteral("dragover")) {
867 bool canDropLink = false;
868 handler->CanDropLink(dragEvent, false, &canDropLink);
869 if (canDropLink) {
870 aEvent->PreventDefault();
871 }
872 } else if (eventType.EqualsLiteral("drop")) {
873 nsIWebNavigation* webnav = static_cast<nsIWebNavigation*>(mWebBrowser);
874
875 nsTArray<RefPtr<nsIDroppedLinkItem>> links;
876 if (webnav && NS_SUCCEEDED(handler->DropLinks(dragEvent, true, links))) {
877 if (links.Length() >= 1) {
878 nsCOMPtr<nsIPrincipal> triggeringPrincipal;
879 handler->GetTriggeringPrincipal(dragEvent,
880 getter_AddRefs(triggeringPrincipal));
881 if (triggeringPrincipal) {
882 nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome =
883 GetWebBrowserChrome();
884 if (webBrowserChrome) {
885 nsCOMPtr<nsIBrowserChild> browserChild =
886 do_QueryInterface(webBrowserChrome);
887 if (browserChild) {
888 nsresult rv = browserChild->RemoteDropLinks(links);
889 return rv;
890 }
891 }
892 nsAutoString url;
893 if (NS_SUCCEEDED(links[0]->GetUrl(url))) {
894 if (!url.IsEmpty()) {
895 #ifndef ANDROID
896 MOZ_ASSERT(triggeringPrincipal,
897 "nsDocShellTreeOwner::HandleEvent: Need a valid "
898 "triggeringPrincipal");
899 #endif
900 LoadURIOptions loadURIOptions;
901 loadURIOptions.mTriggeringPrincipal = triggeringPrincipal;
902 nsCOMPtr<nsIContentSecurityPolicy> csp;
903 handler->GetCSP(dragEvent, getter_AddRefs(csp));
904 loadURIOptions.mCsp = csp;
905 webnav->LoadURI(url, loadURIOptions);
906 }
907 }
908 }
909 }
910 } else {
911 aEvent->StopPropagation();
912 aEvent->PreventDefault();
913 }
914 }
915
916 return NS_OK;
917 }
918
919 already_AddRefed<nsIWebBrowserChrome>
GetWebBrowserChrome()920 nsDocShellTreeOwner::GetWebBrowserChrome() {
921 nsCOMPtr<nsIWebBrowserChrome> chrome;
922 if (mWebBrowserChromeWeak) {
923 chrome = do_QueryReferent(mWebBrowserChromeWeak);
924 } else if (mWebBrowserChrome) {
925 chrome = mWebBrowserChrome;
926 }
927 return chrome.forget();
928 }
929
GetOwnerWin()930 already_AddRefed<nsIEmbeddingSiteWindow> nsDocShellTreeOwner::GetOwnerWin() {
931 nsCOMPtr<nsIEmbeddingSiteWindow> win;
932 if (mWebBrowserChromeWeak) {
933 win = do_QueryReferent(mWebBrowserChromeWeak);
934 } else if (mOwnerWin) {
935 win = mOwnerWin;
936 }
937 return win.forget();
938 }
939
940 already_AddRefed<nsIInterfaceRequestor>
GetOwnerRequestor()941 nsDocShellTreeOwner::GetOwnerRequestor() {
942 nsCOMPtr<nsIInterfaceRequestor> req;
943 if (mWebBrowserChromeWeak) {
944 req = do_QueryReferent(mWebBrowserChromeWeak);
945 } else if (mOwnerRequestor) {
946 req = mOwnerRequestor;
947 }
948 return req.forget();
949 }
950
NS_IMPL_ISUPPORTS(ChromeTooltipListener,nsIDOMEventListener)951 NS_IMPL_ISUPPORTS(ChromeTooltipListener, nsIDOMEventListener)
952
953 ChromeTooltipListener::ChromeTooltipListener(nsWebBrowser* aInBrowser,
954 nsIWebBrowserChrome* aInChrome)
955 : mWebBrowser(aInBrowser),
956 mWebBrowserChrome(aInChrome),
957 mTooltipListenerInstalled(false),
958 mMouseClientX(0),
959 mMouseClientY(0),
960 mMouseScreenX(0),
961 mMouseScreenY(0),
962 mShowingTooltip(false),
963 mTooltipShownOnce(false) {}
964
~ChromeTooltipListener()965 ChromeTooltipListener::~ChromeTooltipListener() {}
966
GetTooltipTextProvider()967 nsITooltipTextProvider* ChromeTooltipListener::GetTooltipTextProvider() {
968 if (!mTooltipTextProvider) {
969 mTooltipTextProvider = do_GetService(NS_TOOLTIPTEXTPROVIDER_CONTRACTID);
970 }
971
972 if (!mTooltipTextProvider) {
973 mTooltipTextProvider =
974 do_GetService(NS_DEFAULTTOOLTIPTEXTPROVIDER_CONTRACTID);
975 }
976
977 return mTooltipTextProvider;
978 }
979
980 // Hook up things to the chrome like context menus and tooltips, if the chrome
981 // has implemented the right interfaces.
982 NS_IMETHODIMP
AddChromeListeners()983 ChromeTooltipListener::AddChromeListeners() {
984 if (!mEventTarget) {
985 GetDOMEventTarget(mWebBrowser, getter_AddRefs(mEventTarget));
986 }
987
988 // Register the appropriate events for tooltips, but only if
989 // the embedding chrome cares.
990 nsresult rv = NS_OK;
991 nsCOMPtr<nsITooltipListener> tooltipListener(
992 do_QueryInterface(mWebBrowserChrome));
993 if (tooltipListener && !mTooltipListenerInstalled) {
994 rv = AddTooltipListener();
995 if (NS_FAILED(rv)) {
996 return rv;
997 }
998 }
999
1000 return rv;
1001 }
1002
1003 // Subscribe to the events that will allow us to track tooltips. We need "mouse"
1004 // for mouseExit, "mouse motion" for mouseMove, and "key" for keyDown. As we
1005 // add the listeners, keep track of how many succeed so we can clean up
1006 // correctly in Release().
1007 NS_IMETHODIMP
AddTooltipListener()1008 ChromeTooltipListener::AddTooltipListener() {
1009 if (mEventTarget) {
1010 nsresult rv = NS_OK;
1011 #ifndef XP_WIN
1012 rv = mEventTarget->AddSystemEventListener(NS_LITERAL_STRING("keydown"),
1013 this, false, false);
1014 NS_ENSURE_SUCCESS(rv, rv);
1015 #endif
1016 rv = mEventTarget->AddSystemEventListener(NS_LITERAL_STRING("mousedown"),
1017 this, false, false);
1018 NS_ENSURE_SUCCESS(rv, rv);
1019 rv = mEventTarget->AddSystemEventListener(NS_LITERAL_STRING("mouseout"),
1020 this, false, false);
1021 NS_ENSURE_SUCCESS(rv, rv);
1022 rv = mEventTarget->AddSystemEventListener(NS_LITERAL_STRING("mousemove"),
1023 this, false, false);
1024 NS_ENSURE_SUCCESS(rv, rv);
1025
1026 mTooltipListenerInstalled = true;
1027 }
1028
1029 return NS_OK;
1030 }
1031
1032 // Unsubscribe from the various things we've hooked up to the window root.
1033 NS_IMETHODIMP
RemoveChromeListeners()1034 ChromeTooltipListener::RemoveChromeListeners() {
1035 HideTooltip();
1036
1037 if (mTooltipListenerInstalled) {
1038 RemoveTooltipListener();
1039 }
1040
1041 mEventTarget = nullptr;
1042
1043 // it really doesn't matter if these fail...
1044 return NS_OK;
1045 }
1046
1047 // Unsubscribe from all the various tooltip events that we were listening to.
1048 NS_IMETHODIMP
RemoveTooltipListener()1049 ChromeTooltipListener::RemoveTooltipListener() {
1050 if (mEventTarget) {
1051 #ifndef XP_WIN
1052 mEventTarget->RemoveSystemEventListener(NS_LITERAL_STRING("keydown"), this,
1053 false);
1054 #endif
1055 mEventTarget->RemoveSystemEventListener(NS_LITERAL_STRING("mousedown"),
1056 this, false);
1057 mEventTarget->RemoveSystemEventListener(NS_LITERAL_STRING("mouseout"), this,
1058 false);
1059 mEventTarget->RemoveSystemEventListener(NS_LITERAL_STRING("mousemove"),
1060 this, false);
1061 mTooltipListenerInstalled = false;
1062 }
1063
1064 return NS_OK;
1065 }
1066
1067 NS_IMETHODIMP
HandleEvent(Event * aEvent)1068 ChromeTooltipListener::HandleEvent(Event* aEvent) {
1069 nsAutoString eventType;
1070 aEvent->GetType(eventType);
1071
1072 if (eventType.EqualsLiteral("mousedown")) {
1073 return HideTooltip();
1074 } else if (eventType.EqualsLiteral("keydown")) {
1075 WidgetKeyboardEvent* keyEvent = aEvent->WidgetEventPtr()->AsKeyboardEvent();
1076 if (!keyEvent->IsModifierKeyEvent()) {
1077 return HideTooltip();
1078 }
1079
1080 return NS_OK;
1081 } else if (eventType.EqualsLiteral("mouseout")) {
1082 // Reset flag so that tooltip will display on the next MouseMove
1083 mTooltipShownOnce = false;
1084 return HideTooltip();
1085 } else if (eventType.EqualsLiteral("mousemove")) {
1086 return MouseMove(aEvent);
1087 }
1088
1089 NS_ERROR("Unexpected event type");
1090 return NS_OK;
1091 }
1092
1093 // If we're a tooltip, fire off a timer to see if a tooltip should be shown. If
1094 // the timer fires, we cache the node in |mPossibleTooltipNode|.
MouseMove(Event * aMouseEvent)1095 nsresult ChromeTooltipListener::MouseMove(Event* aMouseEvent) {
1096 MouseEvent* mouseEvent = aMouseEvent->AsMouseEvent();
1097 if (!mouseEvent) {
1098 return NS_OK;
1099 }
1100
1101 // stash the coordinates of the event so that we can still get back to it from
1102 // within the timer callback. On win32, we'll get a MouseMove event even when
1103 // a popup goes away -- even when the mouse doesn't change position! To get
1104 // around this, we make sure the mouse has really moved before proceeding.
1105 int32_t newMouseX = mouseEvent->ClientX();
1106 int32_t newMouseY = mouseEvent->ClientY();
1107 if (mMouseClientX == newMouseX && mMouseClientY == newMouseY) {
1108 return NS_OK;
1109 }
1110
1111 // Filter out minor mouse movements.
1112 if (mShowingTooltip &&
1113 (abs(mMouseClientX - newMouseX) <= kTooltipMouseMoveTolerance) &&
1114 (abs(mMouseClientY - newMouseY) <= kTooltipMouseMoveTolerance)) {
1115 return NS_OK;
1116 }
1117
1118 mMouseClientX = newMouseX;
1119 mMouseClientY = newMouseY;
1120 mMouseScreenX = mouseEvent->ScreenX(CallerType::System);
1121 mMouseScreenY = mouseEvent->ScreenY(CallerType::System);
1122
1123 if (mTooltipTimer) {
1124 mTooltipTimer->Cancel();
1125 mTooltipTimer = nullptr;
1126 }
1127
1128 if (!mShowingTooltip) {
1129 nsIEventTarget* target = nullptr;
1130 if (nsCOMPtr<EventTarget> eventTarget = aMouseEvent->GetComposedTarget()) {
1131 mPossibleTooltipNode = do_QueryInterface(eventTarget);
1132 nsCOMPtr<nsIGlobalObject> global(eventTarget->GetOwnerGlobal());
1133 if (global) {
1134 target = global->EventTargetFor(TaskCategory::UI);
1135 }
1136 }
1137
1138 if (mPossibleTooltipNode) {
1139 nsresult rv = NS_NewTimerWithFuncCallback(
1140 getter_AddRefs(mTooltipTimer), sTooltipCallback, this,
1141 LookAndFeel::GetInt(LookAndFeel::eIntID_TooltipDelay, 500),
1142 nsITimer::TYPE_ONE_SHOT, "ChromeTooltipListener::MouseMove", target);
1143 if (NS_FAILED(rv)) {
1144 mPossibleTooltipNode = nullptr;
1145 NS_WARNING("Could not create a timer for tooltip tracking");
1146 }
1147 }
1148 } else {
1149 mTooltipShownOnce = true;
1150 return HideTooltip();
1151 }
1152
1153 return NS_OK;
1154 }
1155
1156 // Tell the registered chrome that they should show the tooltip.
1157 NS_IMETHODIMP
ShowTooltip(int32_t aInXCoords,int32_t aInYCoords,const nsAString & aInTipText,const nsAString & aTipDir)1158 ChromeTooltipListener::ShowTooltip(int32_t aInXCoords, int32_t aInYCoords,
1159 const nsAString& aInTipText,
1160 const nsAString& aTipDir) {
1161 nsresult rv = NS_OK;
1162
1163 // do the work to call the client
1164 nsCOMPtr<nsITooltipListener> tooltipListener(
1165 do_QueryInterface(mWebBrowserChrome));
1166 if (tooltipListener) {
1167 rv = tooltipListener->OnShowTooltip(aInXCoords, aInYCoords, aInTipText,
1168 aTipDir);
1169 if (NS_SUCCEEDED(rv)) {
1170 mShowingTooltip = true;
1171 }
1172 }
1173
1174 return rv;
1175 }
1176
1177 // Tell the registered chrome that they should rollup the tooltip
1178 // NOTE: This routine is safe to call even if the popup is already closed.
1179 NS_IMETHODIMP
HideTooltip()1180 ChromeTooltipListener::HideTooltip() {
1181 nsresult rv = NS_OK;
1182
1183 // shut down the relevant timers
1184 if (mTooltipTimer) {
1185 mTooltipTimer->Cancel();
1186 mTooltipTimer = nullptr;
1187 // release tooltip target
1188 mPossibleTooltipNode = nullptr;
1189 mLastDocshell = nullptr;
1190 }
1191
1192 // if we're showing the tip, tell the chrome to hide it
1193 if (mShowingTooltip) {
1194 nsCOMPtr<nsITooltipListener> tooltipListener(
1195 do_QueryInterface(mWebBrowserChrome));
1196 if (tooltipListener) {
1197 rv = tooltipListener->OnHideTooltip();
1198 if (NS_SUCCEEDED(rv)) {
1199 mShowingTooltip = false;
1200 }
1201 }
1202 }
1203
1204 return rv;
1205 }
1206
WebProgressShowedTooltip(nsIWebProgress * aWebProgress)1207 bool ChromeTooltipListener::WebProgressShowedTooltip(
1208 nsIWebProgress* aWebProgress) {
1209 nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(aWebProgress);
1210 nsCOMPtr<nsIDocShell> lastUsed = do_QueryReferent(mLastDocshell);
1211 while (lastUsed) {
1212 if (lastUsed == docshell) {
1213 return true;
1214 }
1215 // We can't use the docshell hierarchy here, because when the parent
1216 // docshell is navigated, the child docshell is disconnected (ie its
1217 // references to the parent are nulled out) despite it still being
1218 // alive here. So we use the document hierarchy instead:
1219 Document* document = lastUsed->GetDocument();
1220 if (document) {
1221 document = document->GetInProcessParentDocument();
1222 }
1223 if (!document) {
1224 break;
1225 }
1226 lastUsed = document->GetDocShell();
1227 }
1228 return false;
1229 }
1230
1231 // A timer callback, fired when the mouse has hovered inside of a frame for the
1232 // appropriate amount of time. Getting to this point means that we should show
1233 // the tooltip, but only after we determine there is an appropriate TITLE
1234 // element.
1235 //
1236 // This relies on certain things being cached into the |aChromeTooltipListener|
1237 // object passed to us by the timer:
1238 // -- the x/y coordinates of the mouse (mMouseClientY, mMouseClientX)
1239 // -- the dom node the user hovered over (mPossibleTooltipNode)
sTooltipCallback(nsITimer * aTimer,void * aChromeTooltipListener)1240 void ChromeTooltipListener::sTooltipCallback(nsITimer* aTimer,
1241 void* aChromeTooltipListener) {
1242 auto self = static_cast<ChromeTooltipListener*>(aChromeTooltipListener);
1243 if (self && self->mPossibleTooltipNode) {
1244 // release tooltip target once done, no matter what we do here.
1245 auto cleanup = MakeScopeExit([&] { self->mPossibleTooltipNode = nullptr; });
1246 if (!self->mPossibleTooltipNode->IsInComposedDoc()) {
1247 return;
1248 }
1249 // Check that the document or its ancestors haven't been replaced.
1250 Document* doc = self->mPossibleTooltipNode->OwnerDoc();
1251 while (doc) {
1252 if (!doc->IsCurrentActiveDocument()) {
1253 return;
1254 }
1255 doc = doc->GetInProcessParentDocument();
1256 }
1257
1258 // The actual coordinates we want to put the tooltip at are relative to the
1259 // toplevel docshell of our mWebBrowser. We know what the screen
1260 // coordinates of the mouse event were, which means we just need the screen
1261 // coordinates of the docshell. Unfortunately, there is no good way to
1262 // find those short of groveling for the presentation in that docshell and
1263 // finding the screen coords of its toplevel widget...
1264 nsCOMPtr<nsIDocShell> docShell =
1265 do_GetInterface(static_cast<nsIWebBrowser*>(self->mWebBrowser));
1266 RefPtr<PresShell> presShell = docShell ? docShell->GetPresShell() : nullptr;
1267
1268 nsIWidget* widget = nullptr;
1269 if (presShell) {
1270 nsViewManager* vm = presShell->GetViewManager();
1271 if (vm) {
1272 nsView* view = vm->GetRootView();
1273 if (view) {
1274 nsPoint offset;
1275 widget = view->GetNearestWidget(&offset);
1276 }
1277 }
1278 }
1279
1280 if (!widget || !docShell || !docShell->GetIsActive()) {
1281 return;
1282 }
1283
1284 // if there is text associated with the node, show the tip and fire
1285 // off a timer to auto-hide it.
1286 nsITooltipTextProvider* tooltipProvider = self->GetTooltipTextProvider();
1287 if (tooltipProvider) {
1288 nsString tooltipText;
1289 nsString directionText;
1290 bool textFound = false;
1291 tooltipProvider->GetNodeText(self->mPossibleTooltipNode,
1292 getter_Copies(tooltipText),
1293 getter_Copies(directionText), &textFound);
1294
1295 if (textFound && (!self->mTooltipShownOnce ||
1296 tooltipText != self->mLastShownTooltipText)) {
1297 LayoutDeviceIntPoint screenDot = widget->WidgetToScreenOffset();
1298 double scaleFactor = 1.0;
1299 if (presShell->GetPresContext()) {
1300 nsDeviceContext* dc = presShell->GetPresContext()->DeviceContext();
1301 scaleFactor = double(AppUnitsPerCSSPixel()) /
1302 dc->AppUnitsPerDevPixelAtUnitFullZoom();
1303 }
1304 // ShowTooltip expects widget-relative position.
1305 self->ShowTooltip(self->mMouseScreenX - screenDot.x / scaleFactor,
1306 self->mMouseScreenY - screenDot.y / scaleFactor,
1307 tooltipText, directionText);
1308 self->mLastShownTooltipText = std::move(tooltipText);
1309 self->mLastDocshell = do_GetWeakReference(
1310 self->mPossibleTooltipNode->OwnerDoc()->GetDocShell());
1311 }
1312 }
1313 }
1314 }
1315