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