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/. */
5 
6 
7 #include "nsWebShellWindow.h"
8 
9 #include "nsLayoutCID.h"
10 #include "nsContentCID.h"
11 #include "nsIWeakReference.h"
12 #include "nsIContentViewer.h"
13 #include "nsIComponentManager.h"
14 #include "nsIServiceManager.h"
15 #include "nsIURL.h"
16 #include "nsIIOService.h"
17 #include "nsIURL.h"
18 #include "nsNetCID.h"
19 #include "nsIStringBundle.h"
20 #include "nsReadableUtils.h"
21 
22 #include "nsContentUtils.h"
23 #include "nsEscape.h"
24 #include "nsPIDOMWindow.h"
25 #include "nsIWebNavigation.h"
26 #include "nsIWindowWatcher.h"
27 
28 #include "nsIDOMXULElement.h"
29 
30 #include "nsWidgetInitData.h"
31 #include "nsWidgetsCID.h"
32 #include "nsIWidget.h"
33 #include "nsIWidgetListener.h"
34 
35 #include "nsIDOMCharacterData.h"
36 #include "nsIDOMNodeList.h"
37 
38 #include "nsITimer.h"
39 #include "nsXULPopupManager.h"
40 
41 
42 #include "nsIDOMXULDocument.h"
43 
44 #include "nsFocusManager.h"
45 
46 #include "nsIWebProgress.h"
47 #include "nsIWebProgressListener.h"
48 
49 #include "nsIDocument.h"
50 #include "nsIDOMDocument.h"
51 #include "nsIDOMNode.h"
52 #include "nsIDOMElement.h"
53 #include "nsIDocumentLoaderFactory.h"
54 #include "nsIObserverService.h"
55 #include "prprf.h"
56 
57 #include "nsIScreenManager.h"
58 #include "nsIScreen.h"
59 
60 #include "nsIContent.h" // for menus
61 #include "nsIScriptSecurityManager.h"
62 
63 // For calculating size
64 #include "nsIPresShell.h"
65 #include "nsPresContext.h"
66 
67 #include "nsIBaseWindow.h"
68 #include "nsIDocShellTreeItem.h"
69 
70 #include "mozilla/Attributes.h"
71 #include "mozilla/DebugOnly.h"
72 #include "mozilla/MouseEvents.h"
73 
74 #include "nsPIWindowRoot.h"
75 
76 #ifdef XP_MACOSX
77 #include "nsINativeMenuService.h"
78 #define USE_NATIVE_MENUS
79 #endif
80 
81 using namespace mozilla;
82 using namespace mozilla::dom;
83 
84 /* Define Class IDs */
85 static NS_DEFINE_CID(kWindowCID,           NS_WINDOW_CID);
86 
87 #define SIZE_PERSISTENCE_TIMEOUT 500 // msec
88 
nsWebShellWindow(uint32_t aChromeFlags)89 nsWebShellWindow::nsWebShellWindow(uint32_t aChromeFlags)
90   : nsXULWindow(aChromeFlags)
91   , mSPTimerLock("nsWebShellWindow.mSPTimerLock")
92   , mWidgetListenerDelegate(this)
93 {
94 }
95 
~nsWebShellWindow()96 nsWebShellWindow::~nsWebShellWindow()
97 {
98   MutexAutoLock lock(mSPTimerLock);
99   if (mSPTimer)
100     mSPTimer->Cancel();
101 }
102 
NS_IMPL_ADDREF_INHERITED(nsWebShellWindow,nsXULWindow)103 NS_IMPL_ADDREF_INHERITED(nsWebShellWindow, nsXULWindow)
104 NS_IMPL_RELEASE_INHERITED(nsWebShellWindow, nsXULWindow)
105 
106 NS_INTERFACE_MAP_BEGIN(nsWebShellWindow)
107   NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
108 NS_INTERFACE_MAP_END_INHERITING(nsXULWindow)
109 
110 nsresult nsWebShellWindow::Initialize(nsIXULWindow* aParent,
111                                       nsIXULWindow* aOpener,
112                                       nsIURI* aUrl,
113                                       int32_t aInitialWidth,
114                                       int32_t aInitialHeight,
115                                       bool aIsHiddenWindow,
116                                       nsITabParent *aOpeningTab,
117                                       mozIDOMWindowProxy *aOpenerWindow,
118                                       nsWidgetInitData& widgetInitData)
119 {
120   nsresult rv;
121   nsCOMPtr<nsIWidget> parentWidget;
122 
123   mIsHiddenWindow = aIsHiddenWindow;
124 
125   int32_t initialX = 0, initialY = 0;
126   nsCOMPtr<nsIBaseWindow> base(do_QueryInterface(aOpener));
127   if (base) {
128     rv = base->GetPositionAndSize(&mOpenerScreenRect.x,
129                                   &mOpenerScreenRect.y,
130                                   &mOpenerScreenRect.width,
131                                   &mOpenerScreenRect.height);
132     if (NS_FAILED(rv)) {
133       mOpenerScreenRect.SetEmpty();
134     } else {
135       double scale;
136       if (NS_SUCCEEDED(base->GetUnscaledDevicePixelsPerCSSPixel(&scale))) {
137         mOpenerScreenRect.x = NSToIntRound(mOpenerScreenRect.x / scale);
138         mOpenerScreenRect.y = NSToIntRound(mOpenerScreenRect.y / scale);
139         mOpenerScreenRect.width = NSToIntRound(mOpenerScreenRect.width / scale);
140         mOpenerScreenRect.height = NSToIntRound(mOpenerScreenRect.height / scale);
141       }
142       initialX = mOpenerScreenRect.x;
143       initialY = mOpenerScreenRect.y;
144       ConstrainToOpenerScreen(&initialX, &initialY);
145     }
146   }
147 
148   // XXX: need to get the default window size from prefs...
149   // Doesn't come from prefs... will come from CSS/XUL/RDF
150   DesktopIntRect deskRect(initialX, initialY, aInitialWidth, aInitialHeight);
151 
152   // Create top level window
153   mWindow = do_CreateInstance(kWindowCID, &rv);
154   if (NS_OK != rv) {
155     return rv;
156   }
157 
158   /* This next bit is troublesome. We carry two different versions of a pointer
159      to our parent window. One is the parent window's widget, which is passed
160      to our own widget. The other is a weak reference we keep here to our
161      parent WebShellWindow. The former is useful to the widget, and we can't
162      trust its treatment of the parent reference because they're platform-
163      specific. The latter is useful to this class.
164        A better implementation would be one in which the parent keeps strong
165      references to its children and closes them before it allows itself
166      to be closed. This would mimic the behaviour of OSes that support
167      top-level child windows in OSes that do not. Later.
168   */
169   nsCOMPtr<nsIBaseWindow> parentAsWin(do_QueryInterface(aParent));
170   if (parentAsWin) {
171     parentAsWin->GetMainWidget(getter_AddRefs(parentWidget));
172     mParentWindow = do_GetWeakReference(aParent);
173   }
174 
175   mWindow->SetWidgetListener(&mWidgetListenerDelegate);
176   rv = mWindow->Create((nsIWidget *)parentWidget, // Parent nsIWidget
177                        nullptr,                   // Native parent widget
178                        deskRect,                  // Widget dimensions
179                        &widgetInitData);          // Widget initialization data
180   NS_ENSURE_SUCCESS(rv, rv);
181 
182   LayoutDeviceIntRect r = mWindow->GetClientBounds();
183   // Match the default background color of content. Important on windows
184   // since we no longer use content child widgets.
185   mWindow->SetBackgroundColor(NS_RGB(255,255,255));
186 
187   // Create web shell
188   mDocShell = do_CreateInstance("@mozilla.org/docshell;1");
189   NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
190 
191   mDocShell->SetOpener(aOpeningTab);
192 
193   // Make sure to set the item type on the docshell _before_ calling
194   // Create() so it knows what type it is.
195   nsCOMPtr<nsIDocShellTreeItem> docShellAsItem(do_QueryInterface(mDocShell));
196   NS_ENSURE_TRUE(docShellAsItem, NS_ERROR_FAILURE);
197   NS_ENSURE_SUCCESS(EnsureChromeTreeOwner(), NS_ERROR_FAILURE);
198 
199   docShellAsItem->SetTreeOwner(mChromeTreeOwner);
200   docShellAsItem->SetItemType(nsIDocShellTreeItem::typeChrome);
201 
202   r.x = r.y = 0;
203   nsCOMPtr<nsIBaseWindow> docShellAsWin(do_QueryInterface(mDocShell));
204   NS_ENSURE_SUCCESS(docShellAsWin->InitWindow(nullptr, mWindow,
205    r.x, r.y, r.width, r.height), NS_ERROR_FAILURE);
206   NS_ENSURE_SUCCESS(docShellAsWin->Create(), NS_ERROR_FAILURE);
207 
208   // Attach a WebProgress listener.during initialization...
209   nsCOMPtr<nsIWebProgress> webProgress(do_GetInterface(mDocShell, &rv));
210   if (webProgress) {
211     webProgress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_NETWORK);
212   }
213 
214   if (aOpenerWindow) {
215     nsPIDOMWindowOuter* window = mDocShell->GetWindow();
216     MOZ_ASSERT(window);
217     window->SetOpenerWindow(nsPIDOMWindowOuter::From(aOpenerWindow), true);
218   }
219 
220   // Eagerly create an about:blank content viewer with the right principal here,
221   // rather than letting it happening in the upcoming call to
222   // SetInitialPrincipalToSubject. This avoids creating the about:blank document
223   // and then blowing it away with a second one, which can cause problems for the
224   // top-level chrome window case. See bug 789773.
225   // Note that we don't accept expanded principals here, similar to
226   // SetInitialPrincipalToSubject.
227   if (nsContentUtils::IsInitialized()) { // Sometimes this happens really early  See bug 793370.
228     MOZ_ASSERT(mDocShell->ItemType() == nsIDocShellTreeItem::typeChrome);
229     nsCOMPtr<nsIPrincipal> principal = nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller();
230     if (nsContentUtils::IsExpandedPrincipal(principal)) {
231       principal = nullptr;
232     }
233     rv = mDocShell->CreateAboutBlankContentViewer(principal);
234     NS_ENSURE_SUCCESS(rv, rv);
235     nsCOMPtr<nsIDocument> doc = mDocShell->GetDocument();
236     NS_ENSURE_TRUE(!!doc, NS_ERROR_FAILURE);
237     doc->SetIsInitialDocument(true);
238   }
239 
240   if (nullptr != aUrl)  {
241     nsCString tmpStr;
242 
243     rv = aUrl->GetSpec(tmpStr);
244     if (NS_FAILED(rv)) return rv;
245 
246     NS_ConvertUTF8toUTF16 urlString(tmpStr);
247     nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
248     NS_ENSURE_TRUE(webNav, NS_ERROR_FAILURE);
249     rv = webNav->LoadURI(urlString.get(),
250                          nsIWebNavigation::LOAD_FLAGS_NONE,
251                          nullptr,
252                          nullptr,
253                          nullptr);
254     NS_ENSURE_SUCCESS(rv, rv);
255   }
256 
257   return rv;
258 }
259 
260 nsIPresShell*
GetPresShell()261 nsWebShellWindow::GetPresShell()
262 {
263   if (!mDocShell)
264     return nullptr;
265 
266   return mDocShell->GetPresShell();
267 }
268 
269 bool
WindowMoved(nsIWidget * aWidget,int32_t x,int32_t y)270 nsWebShellWindow::WindowMoved(nsIWidget* aWidget, int32_t x, int32_t y)
271 {
272   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
273   if (pm) {
274     nsCOMPtr<nsPIDOMWindowOuter> window =
275       mDocShell ? mDocShell->GetWindow() : nullptr;
276     pm->AdjustPopupsOnWindowChange(window);
277   }
278 
279   // Notify all tabs that the widget moved.
280   if (mDocShell && mDocShell->GetWindow()) {
281     nsCOMPtr<EventTarget> eventTarget = mDocShell->GetWindow()->GetTopWindowRoot();
282     nsContentUtils::DispatchChromeEvent(mDocShell->GetDocument(),
283                                         eventTarget,
284                                         NS_LITERAL_STRING("MozUpdateWindowPos"),
285                                         false, false, nullptr);
286   }
287 
288   // Persist position, but not immediately, in case this OS is firing
289   // repeated move events as the user drags the window
290   SetPersistenceTimer(PAD_POSITION);
291   return false;
292 }
293 
294 bool
WindowResized(nsIWidget * aWidget,int32_t aWidth,int32_t aHeight)295 nsWebShellWindow::WindowResized(nsIWidget* aWidget, int32_t aWidth, int32_t aHeight)
296 {
297   nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(mDocShell));
298   if (shellAsWin) {
299     shellAsWin->SetPositionAndSize(0, 0, aWidth, aHeight, 0);
300   }
301   // Persist size, but not immediately, in case this OS is firing
302   // repeated size events as the user drags the sizing handle
303   if (!IsLocked())
304     SetPersistenceTimer(PAD_POSITION | PAD_SIZE | PAD_MISC);
305   return true;
306 }
307 
308 bool
RequestWindowClose(nsIWidget * aWidget)309 nsWebShellWindow::RequestWindowClose(nsIWidget* aWidget)
310 {
311   // Maintain a reference to this as it is about to get destroyed.
312   nsCOMPtr<nsIXULWindow> xulWindow(this);
313 
314   nsCOMPtr<nsPIDOMWindowOuter> window(mDocShell ? mDocShell->GetWindow() : nullptr);
315   nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(window);
316 
317   nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
318   if (!presShell) {
319     mozilla::DebugOnly<bool> dying;
320     MOZ_ASSERT(NS_SUCCEEDED(mDocShell->IsBeingDestroyed(&dying)) && dying,
321                "No presShell, but window is not being destroyed");
322   } else if (eventTarget) {
323     RefPtr<nsPresContext> presContext = presShell->GetPresContext();
324 
325     nsEventStatus status = nsEventStatus_eIgnore;
326     WidgetMouseEvent event(true, eWindowClose, nullptr,
327                            WidgetMouseEvent::eReal);
328     if (NS_SUCCEEDED(eventTarget->DispatchDOMEvent(&event, nullptr, presContext, &status)) &&
329         status == nsEventStatus_eConsumeNoDefault)
330       return false;
331   }
332 
333   Destroy();
334   return false;
335 }
336 
337 void
SizeModeChanged(nsSizeMode sizeMode)338 nsWebShellWindow::SizeModeChanged(nsSizeMode sizeMode)
339 {
340   // An alwaysRaised (or higher) window will hide any newly opened normal
341   // browser windows, so here we just drop a raised window to the normal
342   // zlevel if it's maximized. We make no provision for automatically
343   // re-raising it when restored.
344   if (sizeMode == nsSizeMode_Maximized || sizeMode == nsSizeMode_Fullscreen) {
345     uint32_t zLevel;
346     GetZLevel(&zLevel);
347     if (zLevel > nsIXULWindow::normalZ)
348       SetZLevel(nsIXULWindow::normalZ);
349   }
350   mWindow->SetSizeMode(sizeMode);
351 
352   // Persist mode, but not immediately, because in many (all?)
353   // cases this will merge with the similar call in NS_SIZE and
354   // write the attribute values only once.
355   SetPersistenceTimer(PAD_MISC);
356   nsCOMPtr<nsPIDOMWindowOuter> ourWindow =
357     mDocShell ? mDocShell->GetWindow() : nullptr;
358   if (ourWindow) {
359     MOZ_ASSERT(ourWindow->IsOuterWindow());
360 
361     // Ensure that the fullscreen state is synchronized between
362     // the widget and the outer window object.
363     if (sizeMode == nsSizeMode_Fullscreen) {
364       ourWindow->SetFullScreen(true);
365     }
366     else if (sizeMode != nsSizeMode_Minimized) {
367       if (ourWindow->GetFullScreen()) {
368         // The first SetFullscreenInternal call below ensures that we do
369         // not trigger any fullscreen transition even if the window was
370         // put in fullscreen only for the Fullscreen API. The second
371         // SetFullScreen call ensures that the window really exit from
372         // fullscreen even if it entered fullscreen for both Fullscreen
373         // Mode and Fullscreen API.
374         ourWindow->SetFullscreenInternal(FullscreenReason::ForForceExitFullscreen, false);
375         ourWindow->SetFullScreen(false);
376       }
377     }
378 
379     // And always fire a user-defined sizemodechange event on the window
380     ourWindow->DispatchCustomEvent(NS_LITERAL_STRING("sizemodechange"));
381   }
382 
383   nsIPresShell* presShell;
384   if ((presShell = GetPresShell())) {
385     presShell->GetPresContext()->SizeModeChanged(sizeMode);
386   }
387 
388   // Note the current implementation of SetSizeMode just stores
389   // the new state; it doesn't actually resize. So here we store
390   // the state and pass the event on to the OS. The day is coming
391   // when we'll handle the event here, and the return result will
392   // then need to be different.
393 }
394 
395 void
UIResolutionChanged()396 nsWebShellWindow::UIResolutionChanged()
397 {
398   nsCOMPtr<nsPIDOMWindowOuter> ourWindow =
399     mDocShell ? mDocShell->GetWindow() : nullptr;
400   if (ourWindow) {
401     MOZ_ASSERT(ourWindow->IsOuterWindow());
402     ourWindow->DispatchCustomEvent(NS_LITERAL_STRING("resolutionchange"));
403   }
404 }
405 
406 void
FullscreenChanged(bool aInFullscreen)407 nsWebShellWindow::FullscreenChanged(bool aInFullscreen)
408 {
409   if (mDocShell) {
410     if (nsCOMPtr<nsPIDOMWindowOuter> ourWindow = mDocShell->GetWindow()) {
411       ourWindow->FinishFullscreenChange(aInFullscreen);
412     }
413   }
414 }
415 
416 void
OSToolbarButtonPressed()417 nsWebShellWindow::OSToolbarButtonPressed()
418 {
419   // Keep a reference as setting the chrome flags can fire events.
420   nsCOMPtr<nsIXULWindow> xulWindow(this);
421 
422   // rjc: don't use "nsIWebBrowserChrome::CHROME_EXTRA"
423   //      due to components with multiple sidebar components
424   //      (such as Mail/News, Addressbook, etc)... and frankly,
425   //      Mac IE, OmniWeb, and other Mac OS X apps all work this way
426   uint32_t    chromeMask = (nsIWebBrowserChrome::CHROME_TOOLBAR |
427                             nsIWebBrowserChrome::CHROME_LOCATIONBAR |
428                             nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR);
429 
430   nsCOMPtr<nsIWebBrowserChrome> wbc(do_GetInterface(xulWindow));
431   if (!wbc)
432     return;
433 
434   uint32_t    chromeFlags, newChromeFlags = 0;
435   wbc->GetChromeFlags(&chromeFlags);
436   newChromeFlags = chromeFlags & chromeMask;
437   if (!newChromeFlags)    chromeFlags |= chromeMask;
438   else                    chromeFlags &= (~newChromeFlags);
439   wbc->SetChromeFlags(chromeFlags);
440 }
441 
442 bool
ZLevelChanged(bool aImmediate,nsWindowZ * aPlacement,nsIWidget * aRequestBelow,nsIWidget ** aActualBelow)443 nsWebShellWindow::ZLevelChanged(bool aImmediate, nsWindowZ *aPlacement,
444                                 nsIWidget* aRequestBelow, nsIWidget** aActualBelow)
445 {
446   if (aActualBelow)
447     *aActualBelow = nullptr;
448 
449   return ConstrainToZLevel(aImmediate, aPlacement, aRequestBelow, aActualBelow);
450 }
451 
452 void
WindowActivated()453 nsWebShellWindow::WindowActivated()
454 {
455   nsCOMPtr<nsIXULWindow> xulWindow(this);
456 
457   // focusing the window could cause it to close, so keep a reference to it
458   nsCOMPtr<nsPIDOMWindowOuter> window = mDocShell ? mDocShell->GetWindow() : nullptr;
459   nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
460   if (fm && window)
461     fm->WindowRaised(window);
462 
463   if (mChromeLoaded) {
464     PersistentAttributesDirty(PAD_POSITION | PAD_SIZE | PAD_MISC);
465     SavePersistentAttributes();
466    }
467 }
468 
469 void
WindowDeactivated()470 nsWebShellWindow::WindowDeactivated()
471 {
472   nsCOMPtr<nsIXULWindow> xulWindow(this);
473 
474   nsCOMPtr<nsPIDOMWindowOuter> window =
475     mDocShell ? mDocShell->GetWindow() : nullptr;
476   nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
477   if (fm && window)
478     fm->WindowLowered(window);
479 }
480 
481 #ifdef USE_NATIVE_MENUS
LoadNativeMenus(nsIDOMDocument * aDOMDoc,nsIWidget * aParentWindow)482 static void LoadNativeMenus(nsIDOMDocument *aDOMDoc, nsIWidget *aParentWindow)
483 {
484   nsCOMPtr<nsINativeMenuService> nms = do_GetService("@mozilla.org/widget/nativemenuservice;1");
485   if (!nms) {
486     return;
487   }
488 
489   // Find the menubar tag (if there is more than one, we ignore all but
490   // the first).
491   nsCOMPtr<nsIDOMNodeList> menubarElements;
492   aDOMDoc->GetElementsByTagNameNS(NS_LITERAL_STRING("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"),
493                                   NS_LITERAL_STRING("menubar"),
494                                   getter_AddRefs(menubarElements));
495 
496   nsCOMPtr<nsIDOMNode> menubarNode;
497   if (menubarElements)
498     menubarElements->Item(0, getter_AddRefs(menubarNode));
499 
500   if (menubarNode) {
501     nsCOMPtr<nsIContent> menubarContent(do_QueryInterface(menubarNode));
502     nms->CreateNativeMenuBar(aParentWindow, menubarContent);
503   } else {
504     nms->CreateNativeMenuBar(aParentWindow, nullptr);
505   }
506 }
507 #endif
508 
509 namespace mozilla {
510 
511 class WebShellWindowTimerCallback final : public nsITimerCallback
512 {
513 public:
WebShellWindowTimerCallback(nsWebShellWindow * aWindow)514   explicit WebShellWindowTimerCallback(nsWebShellWindow* aWindow)
515     : mWindow(aWindow)
516   {}
517 
518   NS_DECL_THREADSAFE_ISUPPORTS
519 
Notify(nsITimer * aTimer)520   NS_IMETHOD Notify(nsITimer* aTimer) override
521   {
522     // Although this object participates in a refcount cycle (this -> mWindow
523     // -> mSPTimer -> this), mSPTimer is a one-shot timer and releases this
524     // after it fires.  So we don't need to release mWindow here.
525 
526     mWindow->FirePersistenceTimer();
527     return NS_OK;
528   }
529 
530 private:
~WebShellWindowTimerCallback()531   ~WebShellWindowTimerCallback() {}
532 
533   RefPtr<nsWebShellWindow> mWindow;
534 };
535 
536 NS_IMPL_ISUPPORTS(WebShellWindowTimerCallback, nsITimerCallback)
537 
538 } // namespace mozilla
539 
540 void
SetPersistenceTimer(uint32_t aDirtyFlags)541 nsWebShellWindow::SetPersistenceTimer(uint32_t aDirtyFlags)
542 {
543   MutexAutoLock lock(mSPTimerLock);
544   if (!mSPTimer) {
545     mSPTimer = do_CreateInstance("@mozilla.org/timer;1");
546     if (!mSPTimer) {
547       NS_WARNING("Couldn't create @mozilla.org/timer;1 instance?");
548       return;
549     }
550   }
551 
552   RefPtr<WebShellWindowTimerCallback> callback =
553     new WebShellWindowTimerCallback(this);
554   mSPTimer->InitWithCallback(callback, SIZE_PERSISTENCE_TIMEOUT,
555                              nsITimer::TYPE_ONE_SHOT);
556 
557   PersistentAttributesDirty(aDirtyFlags);
558 }
559 
560 void
FirePersistenceTimer()561 nsWebShellWindow::FirePersistenceTimer()
562 {
563   MutexAutoLock lock(mSPTimerLock);
564   SavePersistentAttributes();
565 }
566 
567 
568 //----------------------------------------
569 // nsIWebProgessListener implementation
570 //----------------------------------------
571 NS_IMETHODIMP
OnProgressChange(nsIWebProgress * aProgress,nsIRequest * aRequest,int32_t aCurSelfProgress,int32_t aMaxSelfProgress,int32_t aCurTotalProgress,int32_t aMaxTotalProgress)572 nsWebShellWindow::OnProgressChange(nsIWebProgress *aProgress,
573                                    nsIRequest *aRequest,
574                                    int32_t aCurSelfProgress,
575                                    int32_t aMaxSelfProgress,
576                                    int32_t aCurTotalProgress,
577                                    int32_t aMaxTotalProgress)
578 {
579   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
580   return NS_OK;
581 }
582 
583 NS_IMETHODIMP
OnStateChange(nsIWebProgress * aProgress,nsIRequest * aRequest,uint32_t aStateFlags,nsresult aStatus)584 nsWebShellWindow::OnStateChange(nsIWebProgress *aProgress,
585                                 nsIRequest *aRequest,
586                                 uint32_t aStateFlags,
587                                 nsresult aStatus)
588 {
589   // If the notification is not about a document finishing, then just
590   // ignore it...
591   if (!(aStateFlags & nsIWebProgressListener::STATE_STOP) ||
592       !(aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK)) {
593     return NS_OK;
594   }
595 
596   if (mChromeLoaded)
597     return NS_OK;
598 
599   // If this document notification is for a frame then ignore it...
600   nsCOMPtr<mozIDOMWindowProxy> eventWin;
601   aProgress->GetDOMWindow(getter_AddRefs(eventWin));
602   auto* eventPWin = nsPIDOMWindowOuter::From(eventWin);
603   if (eventPWin) {
604     nsPIDOMWindowOuter *rootPWin = eventPWin->GetPrivateRoot();
605     if (eventPWin != rootPWin)
606       return NS_OK;
607   }
608 
609   mChromeLoaded = true;
610   mLockedUntilChromeLoad = false;
611 
612 #ifdef USE_NATIVE_MENUS
613   ///////////////////////////////
614   // Find the Menubar DOM  and Load the menus, hooking them up to the loaded commands
615   ///////////////////////////////
616   nsCOMPtr<nsIContentViewer> cv;
617   mDocShell->GetContentViewer(getter_AddRefs(cv));
618   if (cv) {
619     nsCOMPtr<nsIDOMDocument> menubarDOMDoc(do_QueryInterface(cv->GetDocument()));
620     if (menubarDOMDoc)
621       LoadNativeMenus(menubarDOMDoc, mWindow);
622   }
623 #endif // USE_NATIVE_MENUS
624 
625   OnChromeLoaded();
626   LoadContentAreas();
627 
628   return NS_OK;
629 }
630 
631 NS_IMETHODIMP
OnLocationChange(nsIWebProgress * aProgress,nsIRequest * aRequest,nsIURI * aURI,uint32_t aFlags)632 nsWebShellWindow::OnLocationChange(nsIWebProgress *aProgress,
633                                    nsIRequest *aRequest,
634                                    nsIURI *aURI,
635                                    uint32_t aFlags)
636 {
637   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
638   return NS_OK;
639 }
640 
641 NS_IMETHODIMP
OnStatusChange(nsIWebProgress * aWebProgress,nsIRequest * aRequest,nsresult aStatus,const char16_t * aMessage)642 nsWebShellWindow::OnStatusChange(nsIWebProgress* aWebProgress,
643                                  nsIRequest* aRequest,
644                                  nsresult aStatus,
645                                  const char16_t* aMessage)
646 {
647   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
648   return NS_OK;
649 }
650 
651 NS_IMETHODIMP
OnSecurityChange(nsIWebProgress * aWebProgress,nsIRequest * aRequest,uint32_t state)652 nsWebShellWindow::OnSecurityChange(nsIWebProgress *aWebProgress,
653                                    nsIRequest *aRequest,
654                                    uint32_t state)
655 {
656   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
657   return NS_OK;
658 }
659 
660 
661 //----------------------------------------
662 
663 // if the main document URL specified URLs for any content areas, start them loading
LoadContentAreas()664 void nsWebShellWindow::LoadContentAreas() {
665 
666   nsAutoString searchSpec;
667 
668   // fetch the chrome document URL
669   nsCOMPtr<nsIContentViewer> contentViewer;
670   // yes, it's possible for the docshell to be null even this early
671   // see bug 57514.
672   if (mDocShell)
673     mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
674   if (contentViewer) {
675     nsIDocument* doc = contentViewer->GetDocument();
676     if (doc) {
677       nsIURI* mainURL = doc->GetDocumentURI();
678 
679       nsCOMPtr<nsIURL> url = do_QueryInterface(mainURL);
680       if (url) {
681         nsAutoCString search;
682         url->GetQuery(search);
683 
684         AppendUTF8toUTF16(search, searchSpec);
685       }
686     }
687   }
688 
689   // content URLs are specified in the search part of the URL
690   // as <contentareaID>=<escapedURL>[;(repeat)]
691   if (!searchSpec.IsEmpty()) {
692     int32_t     begPos,
693                 eqPos,
694                 endPos;
695     nsString    contentAreaID,
696                 contentURL;
697     char        *urlChar;
698     nsresult rv;
699     for (endPos = 0; endPos < (int32_t)searchSpec.Length(); ) {
700       // extract contentAreaID and URL substrings
701       begPos = endPos;
702       eqPos = searchSpec.FindChar('=', begPos);
703       if (eqPos < 0)
704         break;
705 
706       endPos = searchSpec.FindChar(';', eqPos);
707       if (endPos < 0)
708         endPos = searchSpec.Length();
709       searchSpec.Mid(contentAreaID, begPos, eqPos-begPos);
710       searchSpec.Mid(contentURL, eqPos+1, endPos-eqPos-1);
711       endPos++;
712 
713       // see if we have a docshell with a matching contentAreaID
714       nsCOMPtr<nsIDocShellTreeItem> content;
715       rv = GetContentShellById(contentAreaID.get(), getter_AddRefs(content));
716       if (NS_SUCCEEDED(rv) && content) {
717         nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(content));
718         if (webNav) {
719           urlChar = ToNewCString(contentURL);
720           if (urlChar) {
721             nsUnescape(urlChar);
722             contentURL.AssignWithConversion(urlChar);
723             webNav->LoadURI(contentURL.get(),
724                           nsIWebNavigation::LOAD_FLAGS_NONE,
725                           nullptr,
726                           nullptr,
727                           nullptr);
728             free(urlChar);
729           }
730         }
731       }
732     }
733   }
734 }
735 
736 /**
737  * ExecuteCloseHandler - Run the close handler, if any.
738  * @return true iff we found a close handler to run.
739  */
ExecuteCloseHandler()740 bool nsWebShellWindow::ExecuteCloseHandler()
741 {
742   /* If the event handler closes this window -- a likely scenario --
743      things get deleted out of order without this death grip.
744      (The problem may be the death grip in nsWindow::windowProc,
745      which forces this window's widget to remain alive longer
746      than it otherwise would.) */
747   nsCOMPtr<nsIXULWindow> kungFuDeathGrip(this);
748 
749   nsCOMPtr<EventTarget> eventTarget;
750   if (mDocShell) {
751     eventTarget = do_QueryInterface(mDocShell->GetWindow());
752   }
753 
754   if (eventTarget) {
755     nsCOMPtr<nsIContentViewer> contentViewer;
756     mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
757     if (contentViewer) {
758       RefPtr<nsPresContext> presContext;
759       contentViewer->GetPresContext(getter_AddRefs(presContext));
760 
761       nsEventStatus status = nsEventStatus_eIgnore;
762       WidgetMouseEvent event(true, eWindowClose, nullptr,
763                              WidgetMouseEvent::eReal);
764 
765       nsresult rv =
766         eventTarget->DispatchDOMEvent(&event, nullptr, presContext, &status);
767       if (NS_SUCCEEDED(rv) && status == nsEventStatus_eConsumeNoDefault)
768         return true;
769       // else fall through and return false
770     }
771   }
772 
773   return false;
774 } // ExecuteCloseHandler
775 
ConstrainToOpenerScreen(int32_t * aX,int32_t * aY)776 void nsWebShellWindow::ConstrainToOpenerScreen(int32_t* aX, int32_t* aY)
777 {
778   if (mOpenerScreenRect.IsEmpty()) {
779     *aX = *aY = 0;
780     return;
781   }
782 
783   int32_t left, top, width, height;
784   // Constrain initial positions to the same screen as opener
785   nsCOMPtr<nsIScreenManager> screenmgr = do_GetService("@mozilla.org/gfx/screenmanager;1");
786   if (screenmgr) {
787     nsCOMPtr<nsIScreen> screen;
788     screenmgr->ScreenForRect(mOpenerScreenRect.x, mOpenerScreenRect.y,
789                              mOpenerScreenRect.width, mOpenerScreenRect.height,
790                              getter_AddRefs(screen));
791     if (screen) {
792       screen->GetAvailRectDisplayPix(&left, &top, &width, &height);
793       if (*aX < left || *aX > left + width) {
794         *aX = left;
795       }
796       if (*aY < top || *aY > top + height) {
797         *aY = top;
798       }
799     }
800   }
801 }
802 
803 // nsIBaseWindow
Destroy()804 NS_IMETHODIMP nsWebShellWindow::Destroy()
805 {
806   nsresult rv;
807   nsCOMPtr<nsIWebProgress> webProgress(do_GetInterface(mDocShell, &rv));
808   if (webProgress) {
809     webProgress->RemoveProgressListener(this);
810   }
811 
812   nsCOMPtr<nsIXULWindow> kungFuDeathGrip(this);
813   {
814     MutexAutoLock lock(mSPTimerLock);
815     if (mSPTimer) {
816       mSPTimer->Cancel();
817       SavePersistentAttributes();
818       mSPTimer = nullptr;
819     }
820   }
821   return nsXULWindow::Destroy();
822 }
823 
824 nsIXULWindow*
GetXULWindow()825 nsWebShellWindow::WidgetListenerDelegate::GetXULWindow()
826 {
827   return mWebShellWindow->GetXULWindow();
828 }
829 
830 nsIPresShell*
GetPresShell()831 nsWebShellWindow::WidgetListenerDelegate::GetPresShell()
832 {
833   return mWebShellWindow->GetPresShell();
834 }
835 
836 bool
WindowMoved(nsIWidget * aWidget,int32_t aX,int32_t aY)837 nsWebShellWindow::WidgetListenerDelegate::WindowMoved(
838   nsIWidget* aWidget, int32_t aX, int32_t aY)
839 {
840   RefPtr<nsWebShellWindow> holder = mWebShellWindow;
841   return holder->WindowMoved(aWidget, aX, aY);
842 }
843 
844 bool
WindowResized(nsIWidget * aWidget,int32_t aWidth,int32_t aHeight)845 nsWebShellWindow::WidgetListenerDelegate::WindowResized(
846   nsIWidget* aWidget, int32_t aWidth, int32_t aHeight)
847 {
848   RefPtr<nsWebShellWindow> holder = mWebShellWindow;
849   return holder->WindowResized(aWidget, aWidth, aHeight);
850 }
851 
852 bool
RequestWindowClose(nsIWidget * aWidget)853 nsWebShellWindow::WidgetListenerDelegate::RequestWindowClose(nsIWidget* aWidget)
854 {
855   RefPtr<nsWebShellWindow> holder = mWebShellWindow;
856   return holder->RequestWindowClose(aWidget);
857 }
858 
859 void
SizeModeChanged(nsSizeMode aSizeMode)860 nsWebShellWindow::WidgetListenerDelegate::SizeModeChanged(nsSizeMode aSizeMode)
861 {
862   RefPtr<nsWebShellWindow> holder = mWebShellWindow;
863   holder->SizeModeChanged(aSizeMode);
864 }
865 
866 void
UIResolutionChanged()867 nsWebShellWindow::WidgetListenerDelegate::UIResolutionChanged()
868 {
869   RefPtr<nsWebShellWindow> holder = mWebShellWindow;
870   holder->UIResolutionChanged();
871 }
872 
873 void
FullscreenChanged(bool aInFullscreen)874 nsWebShellWindow::WidgetListenerDelegate::FullscreenChanged(bool aInFullscreen)
875 {
876   RefPtr<nsWebShellWindow> holder = mWebShellWindow;
877   holder->FullscreenChanged(aInFullscreen);
878 }
879 
880 void
OSToolbarButtonPressed()881 nsWebShellWindow::WidgetListenerDelegate::OSToolbarButtonPressed()
882 {
883   RefPtr<nsWebShellWindow> holder = mWebShellWindow;
884   holder->OSToolbarButtonPressed();
885 }
886 
887 bool
ZLevelChanged(bool aImmediate,nsWindowZ * aPlacement,nsIWidget * aRequestBelow,nsIWidget ** aActualBelow)888 nsWebShellWindow::WidgetListenerDelegate::ZLevelChanged(
889   bool aImmediate, nsWindowZ *aPlacement, nsIWidget* aRequestBelow,
890   nsIWidget** aActualBelow)
891 {
892   RefPtr<nsWebShellWindow> holder = mWebShellWindow;
893   return holder->ZLevelChanged(aImmediate,
894                                aPlacement,
895                                aRequestBelow,
896                                aActualBelow);
897 }
898 
899 void
WindowActivated()900 nsWebShellWindow::WidgetListenerDelegate::WindowActivated()
901 {
902   RefPtr<nsWebShellWindow> holder = mWebShellWindow;
903   holder->WindowActivated();
904 }
905 
906 void
WindowDeactivated()907 nsWebShellWindow::WidgetListenerDelegate::WindowDeactivated()
908 {
909   RefPtr<nsWebShellWindow> holder = mWebShellWindow;
910   holder->WindowDeactivated();
911 }
912