Bug 1839338 [wpt PR 40636] - Clone encoded WebRTC audio frame when deserializing...
[gecko.git] / layout / base / nsDocumentViewer.cpp
blob632a55fb62b41d100db32b3c21ff993468b476a8
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/. */
7 /* container for a document and its presentation */
9 #include "gfxContext.h"
10 #include "mozilla/PresShell.h"
11 #include "mozilla/RestyleManager.h"
12 #include "mozilla/ServoStyleSet.h"
13 #include "mozilla/StaticPrefs_print.h"
14 #include "mozilla/Telemetry.h"
15 #include "nsThreadUtils.h"
16 #include "nscore.h"
17 #include "nsCOMPtr.h"
18 #include "nsCRT.h"
19 #include "nsFrameSelection.h"
20 #include "nsString.h"
21 #include "nsReadableUtils.h"
22 #include "nsIContent.h"
23 #include "nsIContentViewer.h"
24 #include "nsIDocumentViewerPrint.h"
25 #include "nsIScreen.h"
26 #include "mozilla/dom/AutoSuppressEventHandlingAndSuspend.h"
27 #include "mozilla/dom/BrowsingContext.h"
28 #include "mozilla/dom/BeforeUnloadEvent.h"
29 #include "mozilla/dom/PopupBlocker.h"
30 #include "mozilla/dom/Document.h"
31 #include "mozilla/dom/DocumentInlines.h"
32 #include "mozilla/dom/DocGroup.h"
33 #include "mozilla/widget/Screen.h"
34 #include "nsPresContext.h"
35 #include "nsIFrame.h"
36 #include "nsIWritablePropertyBag2.h"
37 #include "nsSubDocumentFrame.h"
38 #include "nsGenericHTMLElement.h"
39 #include "nsStubMutationObserver.h"
41 #include "nsISelectionListener.h"
42 #include "mozilla/dom/Selection.h"
43 #include "nsContentUtils.h"
44 #ifdef ACCESSIBILITY
45 # include "mozilla/a11y/DocAccessible.h"
46 #endif
47 #include "mozilla/BasicEvents.h"
48 #include "mozilla/Encoding.h"
49 #include "mozilla/ErrorResult.h"
50 #include "mozilla/Preferences.h"
51 #include "mozilla/SpinEventLoopUntil.h"
52 #include "mozilla/WeakPtr.h"
53 #include "mozilla/StaticPrefs_dom.h"
54 #include "mozilla/StaticPrefs_javascript.h"
55 #include "mozilla/StaticPrefs_fission.h"
56 #include "mozilla/StaticPrefs_print.h"
57 #include "mozilla/StyleSheet.h"
58 #include "mozilla/StyleSheetInlines.h"
60 #include "nsViewManager.h"
61 #include "nsView.h"
63 #include "nsPageSequenceFrame.h"
64 #include "nsNetUtil.h"
65 #include "nsIContentViewerEdit.h"
66 #include "mozilla/css/Loader.h"
67 #include "nsIInterfaceRequestor.h"
68 #include "nsIInterfaceRequestorUtils.h"
69 #include "nsDocShell.h"
70 #include "nsIBaseWindow.h"
71 #include "nsILayoutHistoryState.h"
72 #include "nsCharsetSource.h"
73 #include "mozilla/ReflowInput.h"
74 #include "nsIImageLoadingContent.h"
75 #include "nsCopySupport.h"
76 #include "nsXULPopupManager.h"
78 #include "nsIClipboardHelper.h"
80 #include "nsPIDOMWindow.h"
81 #include "nsGlobalWindow.h"
82 #include "nsDOMNavigationTiming.h"
83 #include "nsPIWindowRoot.h"
84 #include "nsJSEnvironment.h"
85 #include "nsFocusManager.h"
87 #include "nsIScrollableFrame.h"
88 #include "nsStyleSheetService.h"
89 #include "nsILoadContext.h"
90 #include "mozilla/ThrottledEventQueue.h"
91 #include "nsIPromptCollection.h"
92 #include "nsIPromptService.h"
93 #include "imgIContainer.h" // image animation mode constants
94 #include "nsIXULRuntime.h"
95 #include "nsSandboxFlags.h"
97 #include "mozilla/DocLoadingTimelineMarker.h"
99 //--------------------------
100 // Printing Include
101 //---------------------------
102 #ifdef NS_PRINTING
104 # include "nsIWebBrowserPrint.h"
106 # include "nsPrintJob.h"
107 # include "nsDeviceContextSpecProxy.h"
109 // Print Options
110 # include "nsIPrintSettings.h"
111 # include "nsIPrintSettingsService.h"
112 # include "nsISimpleEnumerator.h"
114 #endif // NS_PRINTING
116 // focus
117 #include "nsIDOMEventListener.h"
118 #include "nsISelectionController.h"
120 #include "mozilla/EventDispatcher.h"
121 #include "nsISHEntry.h"
122 #include "nsISHistory.h"
123 #include "nsIWebNavigation.h"
124 #include "mozilla/dom/XMLHttpRequestMainThread.h"
126 // paint forcing
127 #include <stdio.h>
128 #include "mozilla/BasePrincipal.h"
129 #include "mozilla/dom/Element.h"
130 #include "mozilla/dom/Event.h"
131 #include "mozilla/Telemetry.h"
132 #include "mozilla/dom/ScriptLoader.h"
133 #include "mozilla/dom/WindowGlobalChild.h"
135 namespace mozilla {
136 namespace dom {
137 class PrintPreviewResultInfo;
138 } // namespace dom
139 } // namespace mozilla
141 using namespace mozilla;
142 using namespace mozilla::dom;
144 using mozilla::layout::RemotePrintJobChild;
145 using PrintPreviewResolver =
146 std::function<void(const mozilla::dom::PrintPreviewResultInfo&)>;
148 //-----------------------------------------------------
149 // LOGGING
150 #include "LayoutLogging.h"
151 #include "mozilla/Logging.h"
153 extern mozilla::LazyLogModule gPageCacheLog;
155 #ifdef NS_PRINTING
156 mozilla::LazyLogModule gPrintingLog("printing");
158 # define PR_PL(_p1) MOZ_LOG(gPrintingLog, mozilla::LogLevel::Debug, _p1);
159 #endif // NS_PRINTING
161 #define PRT_YESNO(_p) ((_p) ? "YES" : "NO")
162 //-----------------------------------------------------
164 class nsDocumentViewer;
166 // a small delegate class used to avoid circular references
168 class nsDocViewerSelectionListener final : public nsISelectionListener {
169 public:
170 // nsISupports interface...
171 NS_DECL_ISUPPORTS
173 // nsISelectionListerner interface
174 NS_DECL_NSISELECTIONLISTENER
176 explicit nsDocViewerSelectionListener(nsDocumentViewer* aDocViewer)
177 : mDocViewer(aDocViewer), mSelectionWasCollapsed(true) {}
179 void Disconnect() { mDocViewer = nullptr; }
181 protected:
182 virtual ~nsDocViewerSelectionListener() = default;
184 nsDocumentViewer* mDocViewer;
185 bool mSelectionWasCollapsed;
188 /** editor Implementation of the FocusListener interface */
189 class nsDocViewerFocusListener final : public nsIDOMEventListener {
190 public:
191 explicit nsDocViewerFocusListener(nsDocumentViewer* aDocViewer)
192 : mDocViewer(aDocViewer) {}
194 NS_DECL_ISUPPORTS
195 NS_DECL_NSIDOMEVENTLISTENER
197 void Disconnect() { mDocViewer = nullptr; }
199 protected:
200 virtual ~nsDocViewerFocusListener() = default;
202 nsDocumentViewer* mDocViewer;
205 namespace viewer_detail {
208 * Mutation observer for use until we hand ourselves over to our SHEntry.
210 class BFCachePreventionObserver final : public nsStubMutationObserver {
211 public:
212 explicit BFCachePreventionObserver(Document* aDocument)
213 : mDocument(aDocument) {}
215 NS_DECL_ISUPPORTS
217 NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
218 NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
219 NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
220 NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
221 NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
222 NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
224 // Stop observing the document.
225 void Disconnect();
227 private:
228 ~BFCachePreventionObserver() = default;
230 // Helper for the work that needs to happen when mutations happen.
231 void MutationHappened();
233 Document* mDocument; // Weak; we get notified if it dies
236 NS_IMPL_ISUPPORTS(BFCachePreventionObserver, nsIMutationObserver)
238 void BFCachePreventionObserver::CharacterDataChanged(
239 nsIContent* aContent, const CharacterDataChangeInfo&) {
240 if (aContent->IsInNativeAnonymousSubtree()) {
241 return;
243 MutationHappened();
246 void BFCachePreventionObserver::AttributeChanged(Element* aElement,
247 int32_t aNameSpaceID,
248 nsAtom* aAttribute,
249 int32_t aModType,
250 const nsAttrValue* aOldValue) {
251 if (aElement->IsInNativeAnonymousSubtree()) {
252 return;
254 MutationHappened();
257 void BFCachePreventionObserver::ContentAppended(nsIContent* aFirstNewContent) {
258 if (aFirstNewContent->IsInNativeAnonymousSubtree()) {
259 return;
261 MutationHappened();
264 void BFCachePreventionObserver::ContentInserted(nsIContent* aChild) {
265 if (aChild->IsInNativeAnonymousSubtree()) {
266 return;
268 MutationHappened();
271 void BFCachePreventionObserver::ContentRemoved(nsIContent* aChild,
272 nsIContent* aPreviousSibling) {
273 if (aChild->IsInNativeAnonymousSubtree()) {
274 return;
276 MutationHappened();
279 void BFCachePreventionObserver::NodeWillBeDestroyed(nsINode* aNode) {
280 mDocument = nullptr;
283 void BFCachePreventionObserver::Disconnect() {
284 if (mDocument) {
285 mDocument->RemoveMutationObserver(this);
286 // It will no longer tell us when it goes away, so make sure we're
287 // not holding a dangling ref.
288 mDocument = nullptr;
292 void BFCachePreventionObserver::MutationHappened() {
293 MOZ_ASSERT(
294 mDocument,
295 "How can we not have a document but be getting notified for mutations?");
296 mDocument->DisallowBFCaching();
297 Disconnect();
300 } // namespace viewer_detail
302 using viewer_detail::BFCachePreventionObserver;
304 //-------------------------------------------------------------
305 class nsDocumentViewer final : public nsIContentViewer,
306 public nsIContentViewerEdit,
307 public nsIDocumentViewerPrint
308 #ifdef NS_PRINTING
310 public nsIWebBrowserPrint
311 #endif
314 friend class nsDocViewerSelectionListener;
315 friend class nsPagePrintTimer;
316 friend class nsPrintJob;
318 public:
319 nsDocumentViewer();
321 // nsISupports interface...
322 NS_DECL_ISUPPORTS
324 // nsIContentViewer interface...
325 NS_DECL_NSICONTENTVIEWER
327 // nsIContentViewerEdit
328 NS_DECL_NSICONTENTVIEWEREDIT
330 #ifdef NS_PRINTING
331 // nsIWebBrowserPrint
332 NS_DECL_NSIWEBBROWSERPRINT
333 #endif
335 // nsIDocumentViewerPrint Printing Methods
336 NS_DECL_NSIDOCUMENTVIEWERPRINT
338 protected:
339 virtual ~nsDocumentViewer();
341 private:
343 * Creates a view manager, root view, and widget for the root view, setting
344 * mViewManager and mWindow.
345 * @param aSize the initial size in appunits
346 * @param aContainerView the container view to hook our root view up
347 * to as a child, or null if this will be the root view manager
349 nsresult MakeWindow(const nsSize& aSize, nsView* aContainerView);
352 * Create our device context
354 nsresult CreateDeviceContext(nsView* aContainerView);
357 * If aDoCreation is true, this creates the device context, creates a
358 * prescontext if necessary, and calls MakeWindow.
360 * If aForceSetNewDocument is false, then SetNewDocument won't be
361 * called if the window's current document is already mDocument.
363 nsresult InitInternal(nsIWidget* aParentWidget, nsISupports* aState,
364 mozilla::dom::WindowGlobalChild* aActor,
365 const nsIntRect& aBounds, bool aDoCreation,
366 bool aNeedMakeCX = true,
367 bool aForceSetNewDocument = true);
369 * @param aDoInitialReflow set to true if you want to kick off the initial
370 * reflow
372 MOZ_CAN_RUN_SCRIPT_BOUNDARY
373 nsresult InitPresentationStuff(bool aDoInitialReflow);
375 already_AddRefed<nsINode> GetPopupNode();
376 already_AddRefed<nsINode> GetPopupLinkNode();
377 already_AddRefed<nsIImageLoadingContent> GetPopupImageNode();
379 void PrepareToStartLoad(void);
381 nsresult SyncParentSubDocMap();
383 void RemoveFocusListener();
384 void ReinitializeFocusListener();
386 mozilla::dom::Selection* GetDocumentSelection();
388 void DestroyPresShell();
389 void DestroyPresContext();
391 void InvalidatePotentialSubDocDisplayItem();
393 // Whether we should attach to the top level widget. This is true if we
394 // are sharing/recycling a single base widget and not creating multiple
395 // child widgets.
396 bool ShouldAttachToTopLevel();
398 std::tuple<const nsIFrame*, int32_t> GetCurrentSheetFrameAndNumber() const;
400 protected:
401 // Returns the current viewmanager. Might be null.
402 nsViewManager* GetViewManager();
404 void DetachFromTopLevelWidget();
406 // IMPORTANT: The ownership implicit in the following member
407 // variables has been explicitly checked and set using nsCOMPtr
408 // for owning pointers and raw COM interface pointers for weak
409 // (ie, non owning) references. If you add any members to this
410 // class, please make the ownership explicit (pinkerton, scc).
412 WeakPtr<nsDocShell> mContainer; // it owns me!
413 RefPtr<nsDeviceContext> mDeviceContext; // We create and own this baby
415 // the following six items are explicitly in this order
416 // so they will be destroyed in the reverse order (pinkerton, scc)
417 nsCOMPtr<Document> mDocument;
418 nsCOMPtr<nsIWidget> mWindow; // may be null
419 RefPtr<nsViewManager> mViewManager;
420 RefPtr<nsPresContext> mPresContext;
421 RefPtr<PresShell> mPresShell;
423 RefPtr<nsDocViewerSelectionListener> mSelectionListener;
424 RefPtr<nsDocViewerFocusListener> mFocusListener;
426 nsCOMPtr<nsIContentViewer> mPreviousViewer;
427 nsCOMPtr<nsISHEntry> mSHEntry;
428 // Observer that will prevent bfcaching if it gets notified. This
429 // is non-null precisely when mSHEntry is non-null.
430 RefPtr<BFCachePreventionObserver> mBFCachePreventionObserver;
432 nsIWidget* mParentWidget; // purposely won't be ref counted. May be null
433 bool mAttachedToParent; // view is attached to the parent widget
435 nsIntRect mBounds;
437 int16_t mNumURLStarts;
438 int16_t mDestroyBlockedCount;
440 unsigned mStopped : 1;
441 unsigned mLoaded : 1;
442 unsigned mDeferredWindowClose : 1;
443 // document management data
444 // these items are specific to markup documents (html and xml)
445 // may consider splitting these out into a subclass
446 unsigned mIsSticky : 1;
447 unsigned mInPermitUnload : 1;
448 unsigned mInPermitUnloadPrompt : 1;
450 #ifdef NS_PRINTING
451 unsigned mClosingWhilePrinting : 1;
453 # if NS_PRINT_PREVIEW
454 RefPtr<nsPrintJob> mPrintJob;
455 # endif // NS_PRINT_PREVIEW
457 #endif // NS_PRINTING
459 /* character set member data */
460 int32_t mReloadEncodingSource;
461 const Encoding* mReloadEncoding;
463 bool mIsPageMode;
464 bool mInitializedForPrintPreview;
465 bool mHidden;
468 class nsDocumentShownDispatcher : public Runnable {
469 public:
470 explicit nsDocumentShownDispatcher(nsCOMPtr<Document> aDocument)
471 : Runnable("nsDocumentShownDispatcher"), mDocument(aDocument) {}
473 NS_IMETHOD Run() override;
475 private:
476 nsCOMPtr<Document> mDocument;
479 //------------------------------------------------------------------
480 // nsDocumentViewer
481 //------------------------------------------------------------------
483 //------------------------------------------------------------------
484 already_AddRefed<nsIContentViewer> NS_NewContentViewer() {
485 RefPtr<nsDocumentViewer> viewer = new nsDocumentViewer();
486 return viewer.forget();
489 void nsDocumentViewer::PrepareToStartLoad() {
490 MOZ_DIAGNOSTIC_ASSERT(!GetIsPrintPreview(),
491 "Print preview tab should never navigate");
493 mStopped = false;
494 mLoaded = false;
495 mAttachedToParent = false;
496 mDeferredWindowClose = false;
498 #ifdef NS_PRINTING
499 mClosingWhilePrinting = false;
501 // Make sure we have destroyed it and cleared the data member
502 if (mPrintJob) {
503 mPrintJob->Destroy();
504 mPrintJob = nullptr;
507 #endif // NS_PRINTING
510 nsDocumentViewer::nsDocumentViewer()
511 : mParentWidget(nullptr),
512 mAttachedToParent(false),
513 mNumURLStarts(0),
514 mDestroyBlockedCount(0),
515 mStopped(false),
516 mLoaded(false),
517 mDeferredWindowClose(false),
518 mIsSticky(true),
519 mInPermitUnload(false),
520 mInPermitUnloadPrompt(false),
521 #ifdef NS_PRINTING
522 mClosingWhilePrinting(false),
523 #endif // NS_PRINTING
524 mReloadEncodingSource(kCharsetUninitialized),
525 mReloadEncoding(nullptr),
526 mIsPageMode(false),
527 mInitializedForPrintPreview(false),
528 mHidden(false) {
529 PrepareToStartLoad();
532 NS_IMPL_ADDREF(nsDocumentViewer)
533 NS_IMPL_RELEASE(nsDocumentViewer)
535 NS_INTERFACE_MAP_BEGIN(nsDocumentViewer)
536 NS_INTERFACE_MAP_ENTRY(nsIContentViewer)
537 NS_INTERFACE_MAP_ENTRY(nsIContentViewerEdit)
538 NS_INTERFACE_MAP_ENTRY(nsIDocumentViewerPrint)
539 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentViewer)
540 #ifdef NS_PRINTING
541 NS_INTERFACE_MAP_ENTRY(nsIWebBrowserPrint)
542 #endif
543 NS_INTERFACE_MAP_END
545 nsDocumentViewer::~nsDocumentViewer() {
546 if (mDocument) {
547 Close(nullptr);
548 mDocument->Destroy();
551 #ifdef NS_PRINTING
552 if (mPrintJob) {
553 mPrintJob->Destroy();
554 mPrintJob = nullptr;
556 #endif
558 MOZ_RELEASE_ASSERT(mDestroyBlockedCount == 0);
559 NS_ASSERTION(!mPresShell && !mPresContext,
560 "User did not call nsIContentViewer::Destroy");
561 if (mPresShell || mPresContext) {
562 // Make sure we don't hand out a reference to the content viewer to
563 // the SHEntry!
564 mSHEntry = nullptr;
566 Destroy();
569 if (mSelectionListener) {
570 mSelectionListener->Disconnect();
573 RemoveFocusListener();
575 // XXX(?) Revoke pending invalidate events
579 * This method is called by the Document Loader once a document has
580 * been created for a particular data stream... The content viewer
581 * must cache this document for later use when Init(...) is called.
583 * This method is also called when an out of band document.write() happens.
584 * In that case, the document passed in is the same as the previous document.
586 /* virtual */
587 void nsDocumentViewer::LoadStart(Document* aDocument) {
588 MOZ_ASSERT(aDocument);
590 if (!mDocument) {
591 mDocument = aDocument;
595 void nsDocumentViewer::RemoveFocusListener() {
596 if (RefPtr<nsDocViewerFocusListener> oldListener =
597 std::move(mFocusListener)) {
598 oldListener->Disconnect();
599 if (mDocument) {
600 mDocument->RemoveEventListener(u"focus"_ns, oldListener, false);
601 mDocument->RemoveEventListener(u"blur"_ns, oldListener, false);
606 void nsDocumentViewer::ReinitializeFocusListener() {
607 RemoveFocusListener();
608 mFocusListener = new nsDocViewerFocusListener(this);
609 if (mDocument) {
610 mDocument->AddEventListener(u"focus"_ns, mFocusListener, false, false);
611 mDocument->AddEventListener(u"blur"_ns, mFocusListener, false, false);
615 nsresult nsDocumentViewer::SyncParentSubDocMap() {
616 nsCOMPtr<nsIDocShell> docShell(mContainer);
617 if (!docShell) {
618 return NS_OK;
621 nsCOMPtr<nsPIDOMWindowOuter> pwin(docShell->GetWindow());
622 if (!mDocument || !pwin) {
623 return NS_OK;
626 nsCOMPtr<Element> element = pwin->GetFrameElementInternal();
627 if (!element) {
628 return NS_OK;
631 nsCOMPtr<nsIDocShellTreeItem> parent;
632 docShell->GetInProcessParent(getter_AddRefs(parent));
634 nsCOMPtr<nsPIDOMWindowOuter> parent_win =
635 parent ? parent->GetWindow() : nullptr;
636 if (!parent_win) {
637 return NS_OK;
640 nsCOMPtr<Document> parent_doc = parent_win->GetDoc();
641 if (!parent_doc) {
642 return NS_OK;
645 if (mDocument && parent_doc->GetSubDocumentFor(element) != mDocument &&
646 parent_doc->EventHandlingSuppressed()) {
647 mDocument->SuppressEventHandling(parent_doc->EventHandlingSuppressed());
649 return parent_doc->SetSubDocumentFor(element, mDocument);
652 NS_IMETHODIMP
653 nsDocumentViewer::SetContainer(nsIDocShell* aContainer) {
654 mContainer = static_cast<nsDocShell*>(aContainer);
656 // We're loading a new document into the window where this document
657 // viewer lives, sync the parent document's frame element -> sub
658 // document map
660 return SyncParentSubDocMap();
663 NS_IMETHODIMP
664 nsDocumentViewer::GetContainer(nsIDocShell** aResult) {
665 NS_ENSURE_ARG_POINTER(aResult);
667 nsCOMPtr<nsIDocShell> container(mContainer);
668 container.swap(*aResult);
669 return NS_OK;
672 NS_IMETHODIMP
673 nsDocumentViewer::Init(nsIWidget* aParentWidget, const nsIntRect& aBounds,
674 WindowGlobalChild* aActor) {
675 return InitInternal(aParentWidget, nullptr, aActor, aBounds, true);
678 nsresult nsDocumentViewer::InitPresentationStuff(bool aDoInitialReflow) {
679 // We assert this because initializing the pres shell could otherwise cause
680 // re-entrancy into nsDocumentViewer methods, which might cause a different
681 // pres shell to be created. Callers of InitPresentationStuff should ensure
682 // the call is appropriately bounded by an nsAutoScriptBlocker to decide
683 // when it is safe for these re-entrant calls to be made.
684 MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(),
685 "InitPresentationStuff must only be called when scripts are "
686 "blocked");
688 #ifdef NS_PRINTING
689 // When getting printed, either for print or print preview, the print job
690 // takes care of setting up the presentation of the document.
691 if (mPrintJob) {
692 return NS_OK;
694 #endif
696 NS_ASSERTION(!mPresShell, "Someone should have destroyed the presshell!");
698 // Now make the shell for the document
699 nsCOMPtr<Document> doc = mDocument;
700 RefPtr<nsPresContext> presContext = mPresContext;
701 RefPtr<nsViewManager> viewManager = mViewManager;
702 mPresShell = doc->CreatePresShell(presContext, viewManager);
703 if (!mPresShell) {
704 return NS_ERROR_FAILURE;
707 if (aDoInitialReflow) {
708 // Since Initialize() will create frames for *all* items
709 // that are currently in the document tree, we need to flush
710 // any pending notifications to prevent the content sink from
711 // duplicating layout frames for content it has added to the tree
712 // but hasn't notified the document about. (Bug 154018)
714 // Note that we are flushing before we add mPresShell as an observer
715 // to avoid bogus notifications.
716 mDocument->FlushPendingNotifications(FlushType::ContentAndNotify);
719 mPresShell->BeginObservingDocument();
721 // Initialize our view manager
724 int32_t p2a = mPresContext->AppUnitsPerDevPixel();
725 MOZ_ASSERT(
726 p2a ==
727 mPresContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom());
729 nscoord width = p2a * mBounds.width;
730 nscoord height = p2a * mBounds.height;
732 mViewManager->SetWindowDimensions(width, height);
733 mPresContext->SetVisibleArea(nsRect(0, 0, width, height));
734 // We rely on the default zoom not being initialized until here.
735 mPresContext->RecomputeBrowsingContextDependentData();
738 if (mWindow && mDocument->IsTopLevelContentDocument()) {
739 // Set initial safe area insets
740 ScreenIntMargin windowSafeAreaInsets;
741 LayoutDeviceIntRect windowRect = mWindow->GetScreenBounds();
742 nsCOMPtr<nsIScreen> screen = mWindow->GetWidgetScreen();
743 if (screen) {
744 windowSafeAreaInsets = nsContentUtils::GetWindowSafeAreaInsets(
745 screen, mWindow->GetSafeAreaInsets(), windowRect);
748 mPresContext->SetSafeAreaInsets(windowSafeAreaInsets);
751 if (aDoInitialReflow) {
752 RefPtr<PresShell> presShell = mPresShell;
753 // Initial reflow
754 presShell->Initialize();
757 // now register ourselves as a selection listener, so that we get
758 // called when the selection changes in the window
759 if (!mSelectionListener) {
760 mSelectionListener = new nsDocViewerSelectionListener(this);
763 RefPtr<mozilla::dom::Selection> selection = GetDocumentSelection();
764 if (!selection) {
765 return NS_ERROR_FAILURE;
768 selection->AddSelectionListener(mSelectionListener);
770 ReinitializeFocusListener();
772 if (aDoInitialReflow && mDocument) {
773 nsCOMPtr<Document> document = mDocument;
774 document->ScrollToRef();
777 return NS_OK;
780 static nsPresContext* CreatePresContext(Document* aDocument,
781 nsPresContext::nsPresContextType aType,
782 nsView* aContainerView) {
783 if (aContainerView) {
784 return new nsPresContext(aDocument, aType);
786 return new nsRootPresContext(aDocument, aType);
789 //-----------------------------------------------
790 // This method can be used to initial the "presentation"
791 // The aDoCreation indicates whether it should create
792 // all the new objects or just initialize the existing ones
793 nsresult nsDocumentViewer::InitInternal(
794 nsIWidget* aParentWidget, nsISupports* aState, WindowGlobalChild* aActor,
795 const nsIntRect& aBounds, bool aDoCreation, bool aNeedMakeCX /*= true*/,
796 bool aForceSetNewDocument /* = true*/) {
797 // We don't want any scripts to run here. That can cause flushing,
798 // which can cause reentry into initialization of this document viewer,
799 // which would be disastrous.
800 nsAutoScriptBlocker blockScripts;
802 mParentWidget = aParentWidget; // not ref counted
803 mBounds = aBounds;
805 nsresult rv = NS_OK;
806 NS_ENSURE_TRUE(mDocument, NS_ERROR_NULL_POINTER);
808 nsView* containerView = FindContainerView();
810 bool makeCX = false;
811 if (aDoCreation) {
812 nsresult rv = CreateDeviceContext(containerView);
813 NS_ENSURE_SUCCESS(rv, rv);
815 // XXXbz this is a nasty hack to do with the fact that we create
816 // presentations both in Init() and in Show()... Ideally we would only do
817 // it in one place (Show()) and require that callers call init(), open(),
818 // show() in that order or something.
819 if (!mPresContext &&
820 (aParentWidget || containerView || mDocument->IsBeingUsedAsImage() ||
821 (mDocument->GetDisplayDocument() &&
822 mDocument->GetDisplayDocument()->GetPresShell()))) {
823 // Create presentation context
824 if (mIsPageMode) {
825 // Presentation context already created in SetPageModeForTesting which
826 // is calling this method
827 } else {
828 mPresContext = CreatePresContext(
829 mDocument, nsPresContext::eContext_Galley, containerView);
831 NS_ENSURE_TRUE(mPresContext, NS_ERROR_OUT_OF_MEMORY);
833 nsresult rv = mPresContext->Init(mDeviceContext);
834 if (NS_FAILED(rv)) {
835 mPresContext = nullptr;
836 return rv;
839 #if defined(NS_PRINTING) && defined(NS_PRINT_PREVIEW)
840 makeCX = !GetIsPrintPreview() &&
841 aNeedMakeCX; // needs to be true except when we are already in
842 // PP or we are enabling/disabling paginated mode.
843 #else
844 makeCX = true;
845 #endif
848 if (mPresContext) {
849 // Create the ViewManager and Root View...
851 // We must do this before we tell the script global object about
852 // this new document since doing that will cause us to re-enter
853 // into nsSubDocumentFrame code through reflows caused by
854 // FlushPendingNotifications() calls down the road...
856 rv = MakeWindow(nsSize(mPresContext->DevPixelsToAppUnits(aBounds.width),
857 mPresContext->DevPixelsToAppUnits(aBounds.height)),
858 containerView);
859 NS_ENSURE_SUCCESS(rv, rv);
860 Hide();
862 #ifdef NS_PRINT_PREVIEW
863 if (mIsPageMode) {
864 // I'm leaving this in a broken state for the moment; we should
865 // be measuring/scaling with the print device context, not the
866 // screen device context, but this is good enough to allow
867 // printing reftests to work.
868 double pageWidth = 0, pageHeight = 0;
869 mPresContext->GetPrintSettings()->GetEffectivePageSize(&pageWidth,
870 &pageHeight);
871 mPresContext->SetPageSize(
872 nsSize(mPresContext->CSSTwipsToAppUnits(NSToIntFloor(pageWidth)),
873 mPresContext->CSSTwipsToAppUnits(NSToIntFloor(pageHeight))));
874 mPresContext->SetIsRootPaginatedDocument(true);
875 mPresContext->SetPageScale(1.0f);
877 #endif
878 } else {
879 // Avoid leaking the old viewer.
880 if (mPreviousViewer) {
881 mPreviousViewer->Destroy();
882 mPreviousViewer = nullptr;
887 nsCOMPtr<nsIInterfaceRequestor> requestor(mContainer);
888 if (requestor) {
889 // Set script-context-owner in the document
891 nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(requestor);
893 if (window) {
894 nsCOMPtr<Document> curDoc = window->GetExtantDoc();
895 if (aForceSetNewDocument || curDoc != mDocument) {
896 rv = window->SetNewDocument(mDocument, aState, false, aActor);
897 if (NS_FAILED(rv)) {
898 Destroy();
899 return rv;
905 if (aDoCreation && mPresContext) {
906 // The ViewManager and Root View was created above (in
907 // MakeWindow())...
909 rv = InitPresentationStuff(!makeCX);
912 return rv;
915 void nsDocumentViewer::SetNavigationTiming(nsDOMNavigationTiming* timing) {
916 NS_ASSERTION(mDocument, "Must have a document to set navigation timing.");
917 if (mDocument) {
918 mDocument->SetNavigationTiming(timing);
923 // LoadComplete(aStatus)
925 // aStatus - The status returned from loading the document.
927 // This method is called by the container when the document has been
928 // completely loaded.
930 NS_IMETHODIMP
931 nsDocumentViewer::LoadComplete(nsresult aStatus) {
932 /* We need to protect ourself against auto-destruction in case the
933 window is closed while processing the OnLoad event. See bug
934 http://bugzilla.mozilla.org/show_bug.cgi?id=78445 for more
935 explanation.
937 RefPtr<nsDocumentViewer> kungFuDeathGrip(this);
939 // Flush out layout so it's up-to-date by the time onload is called.
940 // Note that this could destroy the window, so do this before
941 // checking for our mDocument and its window.
942 if (mPresShell && !mStopped) {
943 // Hold strong ref because this could conceivably run script
944 RefPtr<PresShell> presShell = mPresShell;
945 presShell->FlushPendingNotifications(FlushType::Layout);
948 nsresult rv = NS_OK;
949 NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
951 // First, get the window from the document...
952 nsCOMPtr<nsPIDOMWindowOuter> window = mDocument->GetWindow();
954 mLoaded = true;
956 // Now, fire either an OnLoad or OnError event to the document...
957 bool restoring = false;
958 // XXXbz imagelib kills off the document load for a full-page image with
959 // NS_ERROR_PARSED_DATA_CACHED if it's in the cache. So we want to treat
960 // that one as a success code; otherwise whether we fire onload for the image
961 // will depend on whether it's cached!
962 if (window &&
963 (NS_SUCCEEDED(aStatus) || aStatus == NS_ERROR_PARSED_DATA_CACHED)) {
964 // If this code changes, the code in nsDocLoader::DocLoaderIsEmpty
965 // that fires load events for document.open() cases might need to
966 // be updated too.
967 nsEventStatus status = nsEventStatus_eIgnore;
968 WidgetEvent event(true, eLoad);
969 event.mFlags.mBubbles = false;
970 event.mFlags.mCancelable = false;
971 // XXX Dispatching to |window|, but using |document| as the target.
972 event.mTarget = mDocument;
974 // If the document presentation is being restored, we don't want to fire
975 // onload to the document content since that would likely confuse scripts
976 // on the page.
978 RefPtr<nsDocShell> docShell = nsDocShell::Cast(window->GetDocShell());
979 NS_ENSURE_TRUE(docShell, NS_ERROR_UNEXPECTED);
981 // Unfortunately, docShell->GetRestoringDocument() might no longer be set
982 // correctly. In particular, it can be false by now if someone took it upon
983 // themselves to block onload from inside restoration and unblock it later.
984 // But we can detect the restoring case very simply: by whether our
985 // document's readyState is COMPLETE.
986 restoring =
987 (mDocument->GetReadyStateEnum() == Document::READYSTATE_COMPLETE);
988 if (!restoring) {
989 NS_ASSERTION(
990 mDocument->GetReadyStateEnum() == Document::READYSTATE_INTERACTIVE ||
991 // test_stricttransportsecurity.html has old-style
992 // docshell-generated about:blank docs reach this code!
993 (mDocument->GetReadyStateEnum() ==
994 Document::READYSTATE_UNINITIALIZED &&
995 NS_IsAboutBlank(mDocument->GetDocumentURI())),
996 "Bad readystate");
997 #ifdef DEBUG
998 bool docShellThinksWeAreRestoring;
999 docShell->GetRestoringDocument(&docShellThinksWeAreRestoring);
1000 MOZ_ASSERT(!docShellThinksWeAreRestoring,
1001 "How can docshell think we are restoring if we don't have a "
1002 "READYSTATE_COMPLETE document?");
1003 #endif // DEBUG
1004 nsCOMPtr<Document> d = mDocument;
1005 mDocument->SetReadyStateInternal(Document::READYSTATE_COMPLETE);
1007 RefPtr<nsDOMNavigationTiming> timing(d->GetNavigationTiming());
1008 if (timing) {
1009 timing->NotifyLoadEventStart();
1012 // Dispatch observer notification to notify observers document load is
1013 // complete.
1014 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1015 if (os) {
1016 nsIPrincipal* principal = d->NodePrincipal();
1017 os->NotifyObservers(ToSupports(d),
1018 principal->IsSystemPrincipal()
1019 ? "chrome-document-loaded"
1020 : "content-document-loaded",
1021 nullptr);
1024 // Notify any devtools about the load.
1025 if (TimelineConsumers::HasConsumer(docShell)) {
1026 TimelineConsumers::AddMarkerForDocShell(
1027 docShell, MakeUnique<DocLoadingTimelineMarker>("document::Load"));
1030 nsPIDOMWindowInner* innerWindow = window->GetCurrentInnerWindow();
1031 RefPtr<DocGroup> docGroup = d->GetDocGroup();
1032 // It is possible that the parent document's load event fires earlier than
1033 // childs' load event, and in this case we need to fire some artificial
1034 // load events to make the parent thinks the load events for child has
1035 // been done
1036 if (innerWindow && DocGroup::TryToLoadIframesInBackground()) {
1037 nsTArray<nsCOMPtr<nsIDocShell>> docShells;
1038 nsCOMPtr<nsIDocShell> container(mContainer);
1039 if (container) {
1040 int32_t count;
1041 container->GetInProcessChildCount(&count);
1042 // We first find all background loading iframes that need to
1043 // fire artificial load events, and instead of firing them as
1044 // soon as we find them, we store them in an array, to prevent
1045 // us from skipping some events.
1046 for (int32_t i = 0; i < count; ++i) {
1047 nsCOMPtr<nsIDocShellTreeItem> child;
1048 container->GetInProcessChildAt(i, getter_AddRefs(child));
1049 nsCOMPtr<nsIDocShell> childIDocShell = do_QueryInterface(child);
1050 RefPtr<nsDocShell> docShell = nsDocShell::Cast(childIDocShell);
1051 if (docShell && docShell->TreatAsBackgroundLoad() &&
1052 docShell->GetDocument()->GetReadyStateEnum() <
1053 Document::READYSTATE_COMPLETE) {
1054 docShells.AppendElement(childIDocShell);
1058 // Re-iterate the stored docShells to fire artificial load events
1059 for (size_t i = 0; i < docShells.Length(); ++i) {
1060 RefPtr<nsDocShell> docShell = nsDocShell::Cast(docShells[i]);
1061 if (docShell && docShell->TreatAsBackgroundLoad() &&
1062 docShell->GetDocument()->GetReadyStateEnum() <
1063 Document::READYSTATE_COMPLETE) {
1064 nsEventStatus status = nsEventStatus_eIgnore;
1065 WidgetEvent event(true, eLoad);
1066 event.mFlags.mBubbles = false;
1067 event.mFlags.mCancelable = false;
1069 nsCOMPtr<nsPIDOMWindowOuter> win = docShell->GetWindow();
1070 nsCOMPtr<Element> element = win->GetFrameElementInternal();
1072 docShell->SetFakeOnLoadDispatched();
1073 EventDispatcher::Dispatch(element, nullptr, &event, nullptr,
1074 &status);
1080 d->SetLoadEventFiring(true);
1081 RefPtr<nsPresContext> presContext = mPresContext;
1082 EventDispatcher::Dispatch(window, presContext, &event, nullptr, &status);
1083 d->SetLoadEventFiring(false);
1085 if (docGroup && docShell->TreatAsBackgroundLoad()) {
1086 docGroup->TryFlushIframePostMessages(docShell->GetOuterWindowID());
1089 if (timing) {
1090 timing->NotifyLoadEventEnd();
1093 if (innerWindow) {
1094 innerWindow->QueuePerformanceNavigationTiming();
1097 } else {
1098 // XXX: Should fire error event to the document...
1100 // If our load was explicitly aborted, then we want to set our
1101 // readyState to COMPLETE, and fire a readystatechange event.
1102 if (aStatus == NS_BINDING_ABORTED && mDocument) {
1103 mDocument->NotifyAbortedLoad();
1107 // Notify the document that it has been shown (regardless of whether
1108 // it was just loaded). Note: mDocument may be null now if the above
1109 // firing of onload caused the document to unload. Or, mDocument may not be
1110 // the "current active" document, if the above firing of onload caused our
1111 // docshell to navigate away. NOTE: In this latter scenario, it's likely that
1112 // we fired pagehide (when navigating away) without ever having fired
1113 // pageshow, and that's pretty broken... Fortunately, this should be rare.
1114 // (It requires us to spin the event loop in onload handler, e.g. via sync
1115 // XHR, in order for the navigation-away to happen before onload completes.)
1116 // We skip firing pageshow if we're currently handling unload, or if loading
1117 // was explicitly aborted.
1118 if (mDocument && mDocument->IsCurrentActiveDocument() &&
1119 aStatus != NS_BINDING_ABORTED) {
1120 // Re-get window, since it might have changed during above firing of onload
1121 window = mDocument->GetWindow();
1122 if (window) {
1123 nsIDocShell* docShell = window->GetDocShell();
1124 bool isInUnload;
1125 if (docShell && NS_SUCCEEDED(docShell->GetIsInUnload(&isInUnload)) &&
1126 !isInUnload) {
1127 mDocument->OnPageShow(restoring, nullptr);
1132 if (!mStopped) {
1133 if (mDocument) {
1134 nsCOMPtr<Document> document = mDocument;
1135 document->ScrollToRef();
1138 // Now that the document has loaded, we can tell the presshell
1139 // to unsuppress painting.
1140 if (mPresShell) {
1141 RefPtr<PresShell> presShell = mPresShell;
1142 presShell->UnsuppressPainting();
1143 // mPresShell could have been removed now, see bug 378682/421432
1144 if (mPresShell) {
1145 mPresShell->LoadComplete();
1150 if (mDocument && !restoring) {
1151 mDocument->LoadEventFired();
1154 // It's probably a good idea to GC soon since we have finished loading.
1155 nsJSContext::PokeGC(
1156 JS::GCReason::LOAD_END,
1157 mDocument ? mDocument->GetWrapperPreserveColor() : nullptr);
1159 #ifdef NS_PRINTING
1160 // Check to see if someone tried to print during the load
1161 if (window) {
1162 auto* outerWin = nsGlobalWindowOuter::Cast(window);
1163 outerWin->StopDelayingPrintingUntilAfterLoad();
1164 if (outerWin->DelayedPrintUntilAfterLoad()) {
1165 // We call into the inner because it ensures there's an active document
1166 // and such, and it also waits until the whole thing completes, which is
1167 // nice because it allows us to close if needed right here.
1168 if (RefPtr inner =
1169 nsGlobalWindowInner::Cast(window->GetCurrentInnerWindow())) {
1170 inner->Print(IgnoreErrors());
1172 if (outerWin->DelayedCloseForPrinting()) {
1173 outerWin->Close();
1175 } else {
1176 MOZ_ASSERT(!outerWin->DelayedCloseForPrinting());
1179 #endif
1181 return rv;
1184 bool nsDocumentViewer::GetLoadCompleted() { return mLoaded; }
1186 bool nsDocumentViewer::GetIsStopped() { return mStopped; }
1188 NS_IMETHODIMP
1189 nsDocumentViewer::PermitUnload(PermitUnloadAction aAction,
1190 bool* aPermitUnload) {
1191 // We're going to be running JS and nested event loops, which could cause our
1192 // DocShell to be destroyed. Make sure we stay alive until the end of the
1193 // function.
1194 RefPtr<nsDocumentViewer> kungFuDeathGrip(this);
1196 if (StaticPrefs::dom_disable_beforeunload()) {
1197 aAction = eDontPromptAndUnload;
1200 *aPermitUnload = true;
1202 RefPtr<BrowsingContext> bc = mContainer->GetBrowsingContext();
1203 if (!bc) {
1204 return NS_OK;
1207 // Per spec, we need to increase the ignore-opens-during-unload counter while
1208 // dispatching the "beforeunload" event on both the document we're currently
1209 // dispatching the event to and the document that we explicitly asked to
1210 // unload.
1211 IgnoreOpensDuringUnload ignoreOpens(mDocument);
1213 bool foundBlocker = false;
1214 bool foundOOPListener = false;
1215 bc->PreOrderWalk([&](BrowsingContext* aBC) {
1216 if (!aBC->IsInProcess()) {
1217 WindowContext* wc = aBC->GetCurrentWindowContext();
1218 if (wc && wc->HasBeforeUnload()) {
1219 foundOOPListener = true;
1221 } else if (aBC->GetDocShell()) {
1222 nsCOMPtr<nsIContentViewer> contentViewer(
1223 aBC->GetDocShell()->GetContentViewer());
1224 if (contentViewer &&
1225 contentViewer->DispatchBeforeUnload() == eRequestBlockNavigation) {
1226 foundBlocker = true;
1231 if (!foundOOPListener) {
1232 if (!foundBlocker) {
1233 return NS_OK;
1235 if (aAction != ePrompt) {
1236 *aPermitUnload = aAction == eDontPromptAndUnload;
1237 return NS_OK;
1241 // NB: we nullcheck mDocument because it might now be dead as a result of
1242 // the event being dispatched.
1243 RefPtr<WindowGlobalChild> wgc(mDocument ? mDocument->GetWindowGlobalChild()
1244 : nullptr);
1245 if (!wgc) {
1246 return NS_OK;
1249 nsAutoSyncOperation sync(mDocument, SyncOperationBehavior::eSuspendInput);
1250 AutoSuppressEventHandlingAndSuspend seh(bc->Group());
1252 mInPermitUnloadPrompt = true;
1254 bool done = false;
1255 wgc->SendCheckPermitUnload(
1256 foundBlocker, aAction,
1257 [&](bool aPermit) {
1258 done = true;
1259 *aPermitUnload = aPermit;
1261 [&](auto) {
1262 // If the prompt aborted, we tell our consumer that it is not allowed
1263 // to unload the page. One reason that prompts abort is that the user
1264 // performed some action that caused the page to unload while our prompt
1265 // was active. In those cases we don't want our consumer to also unload
1266 // the page.
1268 // XXX: Are there other cases where prompts can abort? Is it ok to
1269 // prevent unloading the page in those cases?
1270 done = true;
1271 *aPermitUnload = false;
1274 SpinEventLoopUntil("nsDocumentViewer::PermitUnload"_ns,
1275 [&]() { return done; });
1277 mInPermitUnloadPrompt = false;
1278 return NS_OK;
1281 MOZ_CAN_RUN_SCRIPT_BOUNDARY PermitUnloadResult
1282 nsDocumentViewer::DispatchBeforeUnload() {
1283 AutoDontWarnAboutSyncXHR disableSyncXHRWarning;
1285 if (!mDocument || mInPermitUnload || mInPermitUnloadPrompt) {
1286 return eAllowNavigation;
1289 // First, get the script global object from the document...
1290 RefPtr<nsGlobalWindowOuter> window =
1291 nsGlobalWindowOuter::Cast(mDocument->GetWindow());
1292 if (!window) {
1293 // This is odd, but not fatal
1294 NS_WARNING("window not set for document!");
1295 return eAllowNavigation;
1298 NS_ASSERTION(nsContentUtils::IsSafeToRunScript(), "This is unsafe");
1300 // https://html.spec.whatwg.org/multipage/browsing-the-web.html#prompt-to-unload-a-document
1301 // Create an RAII object on mDocument that will increment the
1302 // should-ignore-opens-during-unload counter on initialization
1303 // and decrement it again when it goes out of score (regardless
1304 // of how we exit this function).
1305 IgnoreOpensDuringUnload ignoreOpens(mDocument);
1307 // Now, fire an BeforeUnload event to the document and see if it's ok
1308 // to unload...
1309 nsPresContext* presContext = mDocument->GetPresContext();
1310 RefPtr<BeforeUnloadEvent> event =
1311 new BeforeUnloadEvent(mDocument, presContext, nullptr);
1312 event->InitEvent(u"beforeunload"_ns, false, true);
1314 // Dispatching to |window|, but using |document| as the target.
1315 event->SetTarget(mDocument);
1316 event->SetTrusted(true);
1318 // In evil cases we might be destroyed while handling the
1319 // onbeforeunload event, don't let that happen. (see also bug#331040)
1320 RefPtr<nsDocumentViewer> kungFuDeathGrip(this);
1323 // Never permit popups from the beforeunload handler, no matter
1324 // how we get here.
1325 AutoPopupStatePusher popupStatePusher(PopupBlocker::openAbused, true);
1327 RefPtr<BrowsingContext> bc = mContainer->GetBrowsingContext();
1328 NS_ASSERTION(bc, "should have a browsing context in document viewer");
1330 // Never permit dialogs from the beforeunload handler
1331 nsGlobalWindowOuter::TemporarilyDisableDialogs disableDialogs(bc);
1333 Document::PageUnloadingEventTimeStamp timestamp(mDocument);
1335 mInPermitUnload = true;
1336 RefPtr<nsPresContext> presContext = mPresContext;
1337 // TODO: Bug 1506441
1338 EventDispatcher::DispatchDOMEvent(MOZ_KnownLive(ToSupports(window)),
1339 nullptr, event, presContext, nullptr);
1340 mInPermitUnload = false;
1343 nsAutoString text;
1344 event->GetReturnValue(text);
1346 // NB: we nullcheck mDocument because it might now be dead as a result of
1347 // the event being dispatched.
1348 if (window->AreDialogsEnabled() && mDocument &&
1349 !(mDocument->GetSandboxFlags() & SANDBOXED_MODALS) &&
1350 (!StaticPrefs::dom_require_user_interaction_for_beforeunload() ||
1351 mDocument->UserHasInteracted()) &&
1352 (event->WidgetEventPtr()->DefaultPrevented() || !text.IsEmpty())) {
1353 return eRequestBlockNavigation;
1355 return eAllowNavigation;
1358 NS_IMETHODIMP
1359 nsDocumentViewer::GetBeforeUnloadFiring(bool* aInEvent) {
1360 *aInEvent = mInPermitUnload;
1361 return NS_OK;
1364 NS_IMETHODIMP
1365 nsDocumentViewer::GetInPermitUnload(bool* aInEvent) {
1366 *aInEvent = mInPermitUnloadPrompt;
1367 return NS_OK;
1370 NS_IMETHODIMP
1371 nsDocumentViewer::PageHide(bool aIsUnload) {
1372 AutoDontWarnAboutSyncXHR disableSyncXHRWarning;
1374 mHidden = true;
1376 if (!mDocument) {
1377 return NS_ERROR_NULL_POINTER;
1380 if (aIsUnload) {
1381 // Poke the GC. The window might be collectable garbage now.
1382 nsJSContext::PokeGC(JS::GCReason::PAGE_HIDE,
1383 mDocument->GetWrapperPreserveColor(),
1384 TimeDuration::FromMilliseconds(
1385 StaticPrefs::javascript_options_gc_delay() * 2));
1388 mDocument->OnPageHide(!aIsUnload, nullptr);
1390 // inform the window so that the focus state is reset.
1391 NS_ENSURE_STATE(mDocument);
1392 nsPIDOMWindowOuter* window = mDocument->GetWindow();
1393 if (window) window->PageHidden();
1395 if (aIsUnload) {
1396 // if Destroy() was called during OnPageHide(), mDocument is nullptr.
1397 NS_ENSURE_STATE(mDocument);
1399 // First, get the window from the document...
1400 RefPtr<nsPIDOMWindowOuter> window = mDocument->GetWindow();
1402 if (!window) {
1403 // Fail if no window is available...
1404 NS_WARNING("window not set for document!");
1405 return NS_ERROR_NULL_POINTER;
1408 // https://html.spec.whatwg.org/multipage/browsing-the-web.html#unload-a-document
1409 // Create an RAII object on mDocument that will increment the
1410 // should-ignore-opens-during-unload counter on initialization
1411 // and decrement it again when it goes out of scope.
1412 IgnoreOpensDuringUnload ignoreOpens(mDocument);
1414 // Now, fire an Unload event to the document...
1415 nsEventStatus status = nsEventStatus_eIgnore;
1416 WidgetEvent event(true, eUnload);
1417 event.mFlags.mBubbles = false;
1418 // XXX Dispatching to |window|, but using |document| as the target.
1419 event.mTarget = mDocument;
1421 // Never permit popups from the unload handler, no matter how we get
1422 // here.
1423 AutoPopupStatePusher popupStatePusher(PopupBlocker::openAbused, true);
1425 Document::PageUnloadingEventTimeStamp timestamp(mDocument);
1427 RefPtr<nsPresContext> presContext = mPresContext;
1428 EventDispatcher::Dispatch(window, presContext, &event, nullptr, &status);
1431 // look for open menupopups and close them after the unload event, in case
1432 // the unload event listeners open any new popups
1433 nsContentUtils::HidePopupsInDocument(mDocument);
1435 return NS_OK;
1438 static void AttachContainerRecurse(nsIDocShell* aShell) {
1439 nsCOMPtr<nsIContentViewer> viewer;
1440 aShell->GetContentViewer(getter_AddRefs(viewer));
1441 if (viewer) {
1442 viewer->SetIsHidden(false);
1443 Document* doc = viewer->GetDocument();
1444 if (doc) {
1445 doc->SetContainer(static_cast<nsDocShell*>(aShell));
1447 if (PresShell* presShell = viewer->GetPresShell()) {
1448 presShell->SetForwardingContainer(WeakPtr<nsDocShell>());
1452 // Now recurse through the children
1453 int32_t childCount;
1454 aShell->GetInProcessChildCount(&childCount);
1455 for (int32_t i = 0; i < childCount; ++i) {
1456 nsCOMPtr<nsIDocShellTreeItem> childItem;
1457 aShell->GetInProcessChildAt(i, getter_AddRefs(childItem));
1458 nsCOMPtr<nsIDocShell> shell = do_QueryInterface(childItem);
1459 AttachContainerRecurse(shell);
1463 NS_IMETHODIMP
1464 nsDocumentViewer::Open(nsISupports* aState, nsISHEntry* aSHEntry) {
1465 NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_INITIALIZED);
1467 if (mDocument) {
1468 mDocument->SetContainer(mContainer);
1471 nsresult rv = InitInternal(mParentWidget, aState, nullptr, mBounds, false);
1472 NS_ENSURE_SUCCESS(rv, rv);
1474 mHidden = false;
1476 if (mPresShell) mPresShell->SetForwardingContainer(WeakPtr<nsDocShell>());
1478 // Rehook the child presentations. The child shells are still in
1479 // session history, so get them from there.
1481 if (aSHEntry) {
1482 nsCOMPtr<nsIDocShellTreeItem> item;
1483 int32_t itemIndex = 0;
1484 while (NS_SUCCEEDED(
1485 aSHEntry->ChildShellAt(itemIndex++, getter_AddRefs(item))) &&
1486 item) {
1487 nsCOMPtr<nsIDocShell> shell = do_QueryInterface(item);
1488 AttachContainerRecurse(shell);
1492 SyncParentSubDocMap();
1494 ReinitializeFocusListener();
1496 // XXX re-enable image animations once that works correctly
1498 PrepareToStartLoad();
1500 // When loading a page from the bfcache with puppet widgets, we do the
1501 // widget attachment here (it is otherwise done in MakeWindow, which is
1502 // called for non-bfcache pages in the history, but not bfcache pages).
1503 // Attachment is necessary, since we get detached when another page
1504 // is browsed to. That is, if we are one page A, then when we go to
1505 // page B, we detach. So page A's view has no widget. If we then go
1506 // back to it, and it is in the bfcache, we will use that view, which
1507 // doesn't have a widget. The attach call here will properly attach us.
1508 if (nsIWidget::UsePuppetWidgets() && mPresContext &&
1509 ShouldAttachToTopLevel()) {
1510 // If the old view is already attached to our parent, detach
1511 DetachFromTopLevelWidget();
1513 nsViewManager* vm = GetViewManager();
1514 MOZ_ASSERT(vm, "no view manager");
1515 nsView* v = vm->GetRootView();
1516 MOZ_ASSERT(v, "no root view");
1517 MOZ_ASSERT(mParentWidget, "no mParentWidget to set");
1518 v->AttachToTopLevelWidget(mParentWidget);
1520 mAttachedToParent = true;
1523 return NS_OK;
1526 NS_IMETHODIMP
1527 nsDocumentViewer::Close(nsISHEntry* aSHEntry) {
1528 // All callers are supposed to call close to break circular
1529 // references. If we do this stuff in the destructor, the
1530 // destructor might never be called (especially if we're being
1531 // used from JS.
1533 mSHEntry = aSHEntry;
1535 // Close is also needed to disable scripts during paint suppression,
1536 // since we transfer the existing global object to the new document
1537 // that is loaded. In the future, the global object may become a proxy
1538 // for an object that can be switched in and out so that we don't need
1539 // to disable scripts during paint suppression.
1541 if (!mDocument) return NS_OK;
1543 if (mSHEntry) {
1544 if (mBFCachePreventionObserver) {
1545 mBFCachePreventionObserver->Disconnect();
1547 mBFCachePreventionObserver = new BFCachePreventionObserver(mDocument);
1548 mDocument->AddMutationObserver(mBFCachePreventionObserver);
1551 #ifdef NS_PRINTING
1552 // A Close was called while we were printing
1553 // so don't clear the ScriptGlobalObject
1554 // or clear the mDocument below
1555 if (mPrintJob && !mClosingWhilePrinting) {
1556 mClosingWhilePrinting = true;
1557 } else
1558 #endif
1560 // out of band cleanup of docshell
1561 mDocument->SetScriptGlobalObject(nullptr);
1563 if (!mSHEntry && mDocument) mDocument->RemovedFromDocShell();
1566 RemoveFocusListener();
1567 return NS_OK;
1570 static void DetachContainerRecurse(nsIDocShell* aShell) {
1571 // Unhook this docshell's presentation
1572 aShell->SynchronizeLayoutHistoryState();
1573 nsCOMPtr<nsIContentViewer> viewer;
1574 aShell->GetContentViewer(getter_AddRefs(viewer));
1575 if (viewer) {
1576 if (Document* doc = viewer->GetDocument()) {
1577 doc->SetContainer(nullptr);
1579 if (PresShell* presShell = viewer->GetPresShell()) {
1580 auto weakShell = static_cast<nsDocShell*>(aShell);
1581 presShell->SetForwardingContainer(weakShell);
1585 // Now recurse through the children
1586 int32_t childCount;
1587 aShell->GetInProcessChildCount(&childCount);
1588 for (int32_t i = 0; i < childCount; ++i) {
1589 nsCOMPtr<nsIDocShellTreeItem> childItem;
1590 aShell->GetInProcessChildAt(i, getter_AddRefs(childItem));
1591 nsCOMPtr<nsIDocShell> shell = do_QueryInterface(childItem);
1592 DetachContainerRecurse(shell);
1596 NS_IMETHODIMP
1597 nsDocumentViewer::Destroy() {
1598 // Don't let the document get unloaded while we are printing.
1599 // this could happen if we hit the back button during printing.
1600 // We also keep the viewer from being cached in session history, since
1601 // we require all documents there to be sanitized.
1602 if (mDestroyBlockedCount != 0) {
1603 return NS_OK;
1606 #ifdef NS_PRINTING
1607 // Here is where we check to see if the document was still being prepared
1608 // for printing when it was asked to be destroy from someone externally
1609 // This usually happens if the document is unloaded while the user is in the
1610 // Print Dialog
1612 // So we flip the bool to remember that the document is going away
1613 // and we can clean up and abort later after returning from the Print Dialog
1614 if (mPrintJob && mPrintJob->CheckBeforeDestroy()) {
1615 return NS_OK;
1617 #endif
1619 // We want to make sure to disconnect mBFCachePreventionObserver before we
1620 // Sanitize() below.
1621 if (mBFCachePreventionObserver) {
1622 mBFCachePreventionObserver->Disconnect();
1623 mBFCachePreventionObserver = nullptr;
1626 if (mSHEntry && mDocument && !mDocument->IsBFCachingAllowed()) {
1627 // Just drop the SHEntry now and pretend like we never even tried to bfcache
1628 // this viewer. This should only happen when someone calls
1629 // DisallowBFCaching() after CanSavePresentation() already ran. Ensure that
1630 // the SHEntry has no viewer and its state is synced up. We want to do this
1631 // via a stack reference, in case those calls mess with our members.
1632 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
1633 ("BFCache not allowed, dropping SHEntry"));
1634 nsCOMPtr<nsISHEntry> shEntry = std::move(mSHEntry);
1635 shEntry->SetContentViewer(nullptr);
1636 shEntry->SyncPresentationState();
1639 // If we were told to put ourselves into session history instead of destroy
1640 // the presentation, do that now.
1641 if (mSHEntry) {
1642 if (mPresShell) mPresShell->Freeze();
1644 // Make sure the presentation isn't torn down by Hide().
1645 mSHEntry->SetSticky(mIsSticky);
1646 mIsSticky = true;
1648 // Remove our root view from the view hierarchy.
1649 if (mPresShell) {
1650 nsViewManager* vm = mPresShell->GetViewManager();
1651 if (vm) {
1652 nsView* rootView = vm->GetRootView();
1654 if (rootView) {
1655 nsView* rootViewParent = rootView->GetParent();
1656 if (rootViewParent) {
1657 nsView* subdocview = rootViewParent->GetParent();
1658 if (subdocview) {
1659 nsIFrame* f = subdocview->GetFrame();
1660 if (f) {
1661 nsSubDocumentFrame* s = do_QueryFrame(f);
1662 if (s) {
1663 s->ClearDisplayItems();
1667 nsViewManager* parentVM = rootViewParent->GetViewManager();
1668 if (parentVM) {
1669 parentVM->RemoveChild(rootView);
1676 Hide();
1678 // This is after Hide() so that the user doesn't see the inputs clear.
1679 if (mDocument) {
1680 mDocument->Sanitize();
1683 // Reverse ownership. Do this *after* calling sanitize so that sanitize
1684 // doesn't cause mutations that make the SHEntry drop the presentation
1686 // Grab a reference to mSHEntry before calling into things like
1687 // SyncPresentationState that might mess with our members.
1688 nsCOMPtr<nsISHEntry> shEntry =
1689 std::move(mSHEntry); // we'll need this below
1691 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
1692 ("Storing content viewer into cache entry"));
1693 shEntry->SetContentViewer(this);
1695 // Always sync the presentation state. That way even if someone screws up
1696 // and shEntry has no window state at this point we'll be ok; we just won't
1697 // cache ourselves.
1698 shEntry->SyncPresentationState();
1699 // XXX Synchronize layout history state to parent once bfcache is supported
1700 // in session-history-in-parent.
1702 // Shut down accessibility for the document before we start to tear it down.
1703 #ifdef ACCESSIBILITY
1704 if (mPresShell) {
1705 a11y::DocAccessible* docAcc = mPresShell->GetDocAccessible();
1706 if (docAcc) {
1707 docAcc->Shutdown();
1710 #endif
1712 // Break the link from the document/presentation to the docshell, so that
1713 // link traversals cannot affect the currently-loaded document.
1714 // When the presentation is restored, Open() and InitInternal() will reset
1715 // these pointers to their original values.
1717 if (mDocument) {
1718 mDocument->SetContainer(nullptr);
1720 if (mPresShell) {
1721 mPresShell->SetForwardingContainer(mContainer);
1724 // Do the same for our children. Note that we need to get the child
1725 // docshells from the SHEntry now; the docshell will have cleared them.
1726 nsCOMPtr<nsIDocShellTreeItem> item;
1727 int32_t itemIndex = 0;
1728 while (NS_SUCCEEDED(
1729 shEntry->ChildShellAt(itemIndex++, getter_AddRefs(item))) &&
1730 item) {
1731 nsCOMPtr<nsIDocShell> shell = do_QueryInterface(item);
1732 DetachContainerRecurse(shell);
1735 return NS_OK;
1738 // The document was not put in the bfcache
1740 // Protect against pres shell destruction running scripts and re-entrantly
1741 // creating a new presentation.
1742 nsAutoScriptBlocker scriptBlocker;
1744 if (mPresShell) {
1745 DestroyPresShell();
1747 if (mDocument) {
1748 mDocument->Destroy();
1749 mDocument = nullptr;
1752 // All callers are supposed to call destroy to break circular
1753 // references. If we do this stuff in the destructor, the
1754 // destructor might never be called (especially if we're being
1755 // used from JS.
1757 #ifdef NS_PRINTING
1758 if (mPrintJob) {
1759 RefPtr<nsPrintJob> printJob = std::move(mPrintJob);
1760 # ifdef NS_PRINT_PREVIEW
1761 if (printJob->CreatedForPrintPreview()) {
1762 printJob->FinishPrintPreview();
1764 # endif
1765 printJob->Destroy();
1766 MOZ_ASSERT(!mPrintJob,
1767 "mPrintJob shouldn't be recreated while destroying it");
1769 #endif
1771 // Avoid leaking the old viewer.
1772 if (mPreviousViewer) {
1773 mPreviousViewer->Destroy();
1774 mPreviousViewer = nullptr;
1777 mDeviceContext = nullptr;
1779 if (mPresContext) {
1780 DestroyPresContext();
1783 mWindow = nullptr;
1784 mViewManager = nullptr;
1785 mContainer = WeakPtr<nsDocShell>();
1787 return NS_OK;
1790 NS_IMETHODIMP
1791 nsDocumentViewer::Stop(void) {
1792 NS_ASSERTION(mDocument, "Stop called too early or too late");
1793 if (mDocument) {
1794 mDocument->StopDocumentLoad();
1797 mStopped = true;
1799 if (!mLoaded && mPresShell) {
1800 // Well, we might as well paint what we have so far.
1801 RefPtr<PresShell> presShell = mPresShell; // bug 378682
1802 presShell->UnsuppressPainting();
1805 return NS_OK;
1808 NS_IMETHODIMP
1809 nsDocumentViewer::GetDOMDocument(Document** aResult) {
1810 NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
1811 nsCOMPtr<Document> document = mDocument;
1812 document.forget(aResult);
1813 return NS_OK;
1816 Document* nsDocumentViewer::GetDocument() { return mDocument; }
1818 nsresult nsDocumentViewer::SetDocument(Document* aDocument) {
1819 // Assumptions:
1821 // 1) this document viewer has been initialized with a call to Init().
1822 // 2) the stylesheets associated with the document have been added
1823 // to the document.
1825 // XXX Right now, this method assumes that the layout of the current
1826 // document hasn't started yet. More cleanup will probably be
1827 // necessary to make this method work for the case when layout *has*
1828 // occurred for the current document.
1829 // That work can happen when and if it is needed.
1831 if (!aDocument) return NS_ERROR_NULL_POINTER;
1833 return SetDocumentInternal(aDocument, false);
1836 NS_IMETHODIMP
1837 nsDocumentViewer::SetDocumentInternal(Document* aDocument,
1838 bool aForceReuseInnerWindow) {
1839 MOZ_ASSERT(aDocument);
1841 // Set new container
1842 aDocument->SetContainer(mContainer);
1844 if (mDocument != aDocument) {
1845 if (aForceReuseInnerWindow) {
1846 // Transfer the navigation timing information to the new document, since
1847 // we're keeping the same inner and hence should really have the same
1848 // timing information.
1849 aDocument->SetNavigationTiming(mDocument->GetNavigationTiming());
1852 if (mDocument &&
1853 (mDocument->IsStaticDocument() || aDocument->IsStaticDocument())) {
1854 nsContentUtils::AddScriptRunner(NewRunnableMethod(
1855 "Document::Destroy", mDocument, &Document::Destroy));
1858 // Clear the list of old child docshells. Child docshells for the new
1859 // document will be constructed as frames are created.
1860 if (!aDocument->IsStaticDocument()) {
1861 nsCOMPtr<nsIDocShell> node(mContainer);
1862 if (node) {
1863 int32_t count;
1864 node->GetInProcessChildCount(&count);
1865 for (int32_t i = 0; i < count; ++i) {
1866 nsCOMPtr<nsIDocShellTreeItem> child;
1867 node->GetInProcessChildAt(0, getter_AddRefs(child));
1868 node->RemoveChild(child);
1873 // Replace the old document with the new one. Do this only when
1874 // the new document really is a new document.
1875 mDocument = aDocument;
1877 // Set the script global object on the new document
1878 nsCOMPtr<nsPIDOMWindowOuter> window =
1879 mContainer ? mContainer->GetWindow() : nullptr;
1880 if (window) {
1881 nsresult rv =
1882 window->SetNewDocument(aDocument, nullptr, aForceReuseInnerWindow);
1883 if (NS_FAILED(rv)) {
1884 Destroy();
1885 return rv;
1890 nsresult rv = SyncParentSubDocMap();
1891 NS_ENSURE_SUCCESS(rv, rv);
1893 // Replace the current pres shell with a new shell for the new document
1895 // Protect against pres shell destruction running scripts and re-entrantly
1896 // creating a new presentation.
1897 nsAutoScriptBlocker scriptBlocker;
1899 if (mPresShell) {
1900 DestroyPresShell();
1903 if (mPresContext) {
1904 DestroyPresContext();
1906 mWindow = nullptr;
1907 rv = InitInternal(mParentWidget, nullptr, nullptr, mBounds, true, true,
1908 false);
1911 return rv;
1914 PresShell* nsDocumentViewer::GetPresShell() { return mPresShell; }
1916 nsPresContext* nsDocumentViewer::GetPresContext() { return mPresContext; }
1918 nsViewManager* nsDocumentViewer::GetViewManager() { return mViewManager; }
1920 NS_IMETHODIMP
1921 nsDocumentViewer::GetBounds(nsIntRect& aResult) {
1922 NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
1923 aResult = mBounds;
1924 return NS_OK;
1927 nsIContentViewer* nsDocumentViewer::GetPreviousViewer() {
1928 return mPreviousViewer;
1931 void nsDocumentViewer::SetPreviousViewer(nsIContentViewer* aViewer) {
1932 // NOTE: |Show| sets |mPreviousViewer| to null without calling this
1933 // function.
1935 if (aViewer) {
1936 NS_ASSERTION(!mPreviousViewer,
1937 "can't set previous viewer when there already is one");
1939 // In a multiple chaining situation (which occurs when running a thrashing
1940 // test like i-bench or jrgm's tests with no delay), we can build up a
1941 // whole chain of viewers. In order to avoid this, we always set our
1942 // previous viewer to the MOST previous viewer in the chain, and then dump
1943 // the intermediate link from the chain. This ensures that at most only 2
1944 // documents are alive and undestroyed at any given time (the one that is
1945 // showing and the one that is loading with painting suppressed). It's very
1946 // important that if this ever gets changed the code before the
1947 // RestorePresentation call in nsDocShell::InternalLoad be changed
1948 // accordingly.
1950 // Make sure we hold a strong ref to prevViewer here, since we'll
1951 // tell aViewer to drop it.
1952 nsCOMPtr<nsIContentViewer> prevViewer = aViewer->GetPreviousViewer();
1953 if (prevViewer) {
1954 aViewer->SetPreviousViewer(nullptr);
1955 aViewer->Destroy();
1956 return SetPreviousViewer(prevViewer);
1960 mPreviousViewer = aViewer;
1963 NS_IMETHODIMP
1964 nsDocumentViewer::SetBoundsWithFlags(const nsIntRect& aBounds,
1965 uint32_t aFlags) {
1966 NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
1968 bool boundsChanged = !mBounds.IsEqualEdges(aBounds);
1969 mBounds = aBounds;
1971 if (mWindow && !mAttachedToParent) {
1972 // Resize the widget, but don't trigger repaint. Layout will generate
1973 // repaint requests during reflow.
1974 mWindow->Resize(aBounds.x, aBounds.y, aBounds.width, aBounds.height, false);
1975 } else if (mPresContext && mViewManager) {
1976 // Ensure presContext's deviceContext is up to date, as we sometimes get
1977 // here before a resolution-change notification has been fully handled
1978 // during display configuration changes, especially when there are lots
1979 // of windows/widgets competing to handle the notifications.
1980 // (See bug 1154125.)
1981 if (mPresContext->DeviceContext()->CheckDPIChange()) {
1982 mPresContext->UIResolutionChangedSync();
1985 int32_t p2a = mPresContext->AppUnitsPerDevPixel();
1986 nscoord width = NSIntPixelsToAppUnits(mBounds.width, p2a);
1987 nscoord height = NSIntPixelsToAppUnits(mBounds.height, p2a);
1988 nsView* rootView = mViewManager->GetRootView();
1989 if (boundsChanged && rootView) {
1990 nsRect viewDims = rootView->GetDimensions();
1991 // If the view/frame tree and prescontext visible area already has the new
1992 // size but we did not, then it's likely that we got reflowed in response
1993 // to a call to GetContentSize. Thus there is a disconnect between the
1994 // size on the document viewer/docshell/containing widget and view
1995 // tree/frame tree/prescontext visible area). SetWindowDimensions compares
1996 // to the root view dimenstions to determine if it needs to do anything;
1997 // if they are the same as the new size it won't do anything, but we still
1998 // need to invalidate because what we want to draw to the screen has
1999 // changed.
2000 if (viewDims.width == width && viewDims.height == height) {
2001 nsIFrame* f = rootView->GetFrame();
2002 if (f) {
2003 f->InvalidateFrame();
2008 mViewManager->SetWindowDimensions(
2009 width, height, !!(aFlags & nsIContentViewer::eDelayResize));
2012 // If there's a previous viewer, it's the one that's actually showing,
2013 // so be sure to resize it as well so it paints over the right area.
2014 // This may slow down the performance of the new page load, but resize
2015 // during load is also probably a relatively unusual condition
2016 // relating to things being hidden while something is loaded. It so
2017 // happens that Firefox does this a good bit with its infobar, and it
2018 // looks ugly if we don't do this.
2019 if (mPreviousViewer) {
2020 nsCOMPtr<nsIContentViewer> previousViewer = mPreviousViewer;
2021 previousViewer->SetBounds(aBounds);
2024 return NS_OK;
2027 NS_IMETHODIMP
2028 nsDocumentViewer::SetBounds(const nsIntRect& aBounds) {
2029 return SetBoundsWithFlags(aBounds, 0);
2032 NS_IMETHODIMP
2033 nsDocumentViewer::Move(int32_t aX, int32_t aY) {
2034 NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
2035 mBounds.MoveTo(aX, aY);
2036 if (mWindow) {
2037 mWindow->Move(aX, aY);
2039 return NS_OK;
2042 NS_IMETHODIMP
2043 nsDocumentViewer::Show() {
2044 NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
2046 // We don't need the previous viewer anymore since we're not
2047 // displaying it.
2048 if (mPreviousViewer) {
2049 // This little dance *may* only be to keep
2050 // PresShell::EndObservingDocument happy, but I'm not sure.
2051 nsCOMPtr<nsIContentViewer> prevViewer(mPreviousViewer);
2052 mPreviousViewer = nullptr;
2053 prevViewer->Destroy();
2055 // Make sure we don't have too many cached ContentViewers
2056 nsCOMPtr<nsIDocShellTreeItem> treeItem(mContainer);
2057 if (treeItem) {
2058 // We need to find the root DocShell since only that object has an
2059 // SHistory and we need the SHistory to evict content viewers
2060 nsCOMPtr<nsIDocShellTreeItem> root;
2061 treeItem->GetInProcessSameTypeRootTreeItem(getter_AddRefs(root));
2062 nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(root);
2063 RefPtr<ChildSHistory> history = webNav->GetSessionHistory();
2064 if (!mozilla::SessionHistoryInParent() && history) {
2065 int32_t prevIndex, loadedIndex;
2066 nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(treeItem);
2067 docShell->GetPreviousEntryIndex(&prevIndex);
2068 docShell->GetLoadedEntryIndex(&loadedIndex);
2069 MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
2070 ("About to evict content viewers: prev=%d, loaded=%d",
2071 prevIndex, loadedIndex));
2072 history->LegacySHistory()->EvictOutOfRangeContentViewers(loadedIndex);
2077 if (mWindow) {
2078 // When attached to a top level xul window, we do not need to call
2079 // Show on the widget. Underlying window management code handles
2080 // this when the window is initialized.
2081 if (!mAttachedToParent) {
2082 mWindow->Show(true);
2086 // Hold on to the document so we can use it after the script blocker below
2087 // has been released (which might re-entrantly call into other
2088 // nsDocumentViewer methods).
2089 nsCOMPtr<Document> document = mDocument;
2091 if (mDocument && !mPresShell) {
2092 // The InitPresentationStuff call below requires a script blocker, because
2093 // its PresShell::Initialize call can cause scripts to run and therefore
2094 // re-entrant calls to nsDocumentViewer methods to be made.
2095 nsAutoScriptBlocker scriptBlocker;
2097 NS_ASSERTION(!mWindow, "Window already created but no presshell?");
2099 nsCOMPtr<nsIBaseWindow> base_win(mContainer);
2100 if (base_win) {
2101 base_win->GetParentWidget(&mParentWidget);
2102 if (mParentWidget) {
2103 // GetParentWidget AddRefs, but mParentWidget is weak
2104 mParentWidget->Release();
2108 nsView* containerView = FindContainerView();
2110 nsresult rv = CreateDeviceContext(containerView);
2111 NS_ENSURE_SUCCESS(rv, rv);
2113 // Create presentation context
2114 NS_ASSERTION(!mPresContext,
2115 "Shouldn't have a prescontext if we have no shell!");
2116 mPresContext = CreatePresContext(mDocument, nsPresContext::eContext_Galley,
2117 containerView);
2118 NS_ENSURE_TRUE(mPresContext, NS_ERROR_OUT_OF_MEMORY);
2120 rv = mPresContext->Init(mDeviceContext);
2121 if (NS_FAILED(rv)) {
2122 mPresContext = nullptr;
2123 return rv;
2126 rv = MakeWindow(nsSize(mPresContext->DevPixelsToAppUnits(mBounds.width),
2127 mPresContext->DevPixelsToAppUnits(mBounds.height)),
2128 containerView);
2129 if (NS_FAILED(rv)) return rv;
2131 if (mPresContext) {
2132 Hide();
2134 rv = InitPresentationStuff(mDocument->MayStartLayout());
2137 // If we get here the document load has already started and the
2138 // window is shown because some JS on the page caused it to be
2139 // shown...
2141 if (mPresShell) {
2142 RefPtr<PresShell> presShell = mPresShell; // bug 378682
2143 presShell->UnsuppressPainting();
2147 // Notify observers that a new page has been shown. This will get run
2148 // from the event loop after we actually draw the page.
2149 RefPtr<nsDocumentShownDispatcher> event =
2150 new nsDocumentShownDispatcher(document);
2151 document->Dispatch(TaskCategory::Other, event.forget());
2153 return NS_OK;
2156 NS_IMETHODIMP
2157 nsDocumentViewer::Hide() {
2158 if (!mAttachedToParent && mWindow) {
2159 mWindow->Show(false);
2162 if (!mPresShell) return NS_OK;
2164 NS_ASSERTION(mPresContext, "Can't have a presshell and no prescontext!");
2166 // Avoid leaking the old viewer.
2167 if (mPreviousViewer) {
2168 mPreviousViewer->Destroy();
2169 mPreviousViewer = nullptr;
2172 if (mIsSticky) {
2173 // This window is sticky, that means that it might be shown again
2174 // and we don't want the presshell n' all that to be thrown away
2175 // just because the window is hidden.
2177 return NS_OK;
2180 nsCOMPtr<nsIDocShell> docShell(mContainer);
2181 if (docShell) {
2182 #ifdef DEBUG
2183 nsCOMPtr<nsIContentViewer> currentViewer;
2184 docShell->GetContentViewer(getter_AddRefs(currentViewer));
2185 MOZ_ASSERT(currentViewer == this);
2186 #endif
2187 nsCOMPtr<nsILayoutHistoryState> layoutState;
2188 mPresShell->CaptureHistoryState(getter_AddRefs(layoutState));
2191 // Do not run ScriptRunners queued by DestroyPresShell() in the intermediate
2192 // state before we're done destroying PresShell, PresContext, ViewManager,
2193 // etc.
2194 nsAutoScriptBlocker scriptBlocker;
2196 DestroyPresShell();
2198 DestroyPresContext();
2200 mViewManager = nullptr;
2201 mWindow = nullptr;
2202 mDeviceContext = nullptr;
2203 mParentWidget = nullptr;
2205 nsCOMPtr<nsIBaseWindow> base_win(mContainer);
2207 if (base_win && !mAttachedToParent) {
2208 base_win->SetParentWidget(nullptr);
2211 return NS_OK;
2214 NS_IMETHODIMP
2215 nsDocumentViewer::GetSticky(bool* aSticky) {
2216 *aSticky = mIsSticky;
2218 return NS_OK;
2221 NS_IMETHODIMP
2222 nsDocumentViewer::SetSticky(bool aSticky) {
2223 mIsSticky = aSticky;
2225 return NS_OK;
2228 NS_IMETHODIMP
2229 nsDocumentViewer::ClearHistoryEntry() {
2230 if (mDocument) {
2231 nsJSContext::PokeGC(JS::GCReason::PAGE_HIDE,
2232 mDocument->GetWrapperPreserveColor(),
2233 TimeDuration::FromMilliseconds(
2234 StaticPrefs::javascript_options_gc_delay() * 2));
2237 mSHEntry = nullptr;
2238 return NS_OK;
2241 //-------------------------------------------------------
2243 nsresult nsDocumentViewer::MakeWindow(const nsSize& aSize,
2244 nsView* aContainerView) {
2245 if (GetIsPrintPreview()) {
2246 return NS_OK;
2249 bool shouldAttach = ShouldAttachToTopLevel();
2251 if (shouldAttach) {
2252 // If the old view is already attached to our parent, detach
2253 DetachFromTopLevelWidget();
2256 mViewManager = new nsViewManager();
2258 nsDeviceContext* dx = mPresContext->DeviceContext();
2260 nsresult rv = mViewManager->Init(dx);
2261 if (NS_FAILED(rv)) return rv;
2263 // The root view is always at 0,0.
2264 nsRect tbounds(nsPoint(0, 0), aSize);
2265 // Create a view
2266 nsView* view = mViewManager->CreateView(tbounds, aContainerView);
2267 if (!view) return NS_ERROR_OUT_OF_MEMORY;
2269 // Create a widget if we were given a parent widget or don't have a
2270 // container view that we can hook up to without a widget.
2271 // Don't create widgets for ResourceDocs (external resources & svg images),
2272 // because when they're displayed, they're painted into *another* document's
2273 // widget.
2274 if (!mDocument->IsResourceDoc() && (mParentWidget || !aContainerView)) {
2275 // pass in a native widget to be the parent widget ONLY if the view
2276 // hierarchy will stand alone. otherwise the view will find its own parent
2277 // widget and "do the right thing" to establish a parent/child widget
2278 // relationship
2279 widget::InitData initData;
2280 widget::InitData* initDataPtr;
2281 if (!mParentWidget) {
2282 initDataPtr = &initData;
2283 initData.mWindowType = widget::WindowType::Invisible;
2284 } else {
2285 initDataPtr = nullptr;
2288 if (shouldAttach) {
2289 // Reuse the top level parent widget.
2290 rv = view->AttachToTopLevelWidget(mParentWidget);
2291 mAttachedToParent = true;
2292 } else if (!aContainerView && mParentWidget) {
2293 rv = view->CreateWidgetForParent(mParentWidget, initDataPtr, true, false);
2294 } else {
2295 rv = view->CreateWidget(initDataPtr, true, false);
2297 if (NS_FAILED(rv)) return rv;
2300 // Setup hierarchical relationship in view manager
2301 mViewManager->SetRootView(view);
2303 mWindow = view->GetWidget();
2305 // This SetFocus is necessary so the Arrow Key and Page Key events
2306 // go to the scrolled view as soon as the Window is created instead of going
2307 // to the browser window (this enables keyboard scrolling of the document)
2308 // mWindow->SetFocus();
2310 return rv;
2313 void nsDocumentViewer::DetachFromTopLevelWidget() {
2314 if (mViewManager) {
2315 nsView* oldView = mViewManager->GetRootView();
2316 if (oldView && oldView->IsAttachedToTopLevel()) {
2317 oldView->DetachFromTopLevelWidget();
2320 mAttachedToParent = false;
2323 nsView* nsDocumentViewer::FindContainerView() {
2324 if (!mContainer) {
2325 return nullptr;
2328 nsCOMPtr<nsIDocShell> docShell(mContainer);
2329 nsCOMPtr<nsPIDOMWindowOuter> pwin(docShell->GetWindow());
2330 if (!pwin) {
2331 return nullptr;
2334 nsCOMPtr<Element> containerElement = pwin->GetFrameElementInternal();
2335 if (!containerElement) {
2336 return nullptr;
2339 nsIFrame* subdocFrame = containerElement->GetPrimaryFrame();
2340 if (!subdocFrame) {
2341 // XXX Silenced by default in bug 1175289
2342 LAYOUT_WARNING("Subdocument container has no frame");
2343 return nullptr;
2346 // Check subdocFrame just to be safe. If this somehow fails we treat that as
2347 // display:none, the document is not displayed.
2348 if (!subdocFrame->IsSubDocumentFrame()) {
2349 NS_WARNING_ASSERTION(subdocFrame->Type() == LayoutFrameType::None,
2350 "Subdocument container has non-subdocument frame");
2351 return nullptr;
2354 NS_ASSERTION(subdocFrame->GetView(), "Subdoc frames must have views");
2355 return static_cast<nsSubDocumentFrame*>(subdocFrame)->EnsureInnerView();
2358 nsresult nsDocumentViewer::CreateDeviceContext(nsView* aContainerView) {
2359 MOZ_ASSERT(!mPresShell && !mWindow,
2360 "This will screw up our existing presentation");
2361 MOZ_ASSERT(mDocument, "Gotta have a document here");
2363 Document* doc = mDocument->GetDisplayDocument();
2364 if (doc) {
2365 NS_ASSERTION(!aContainerView,
2366 "External resource document embedded somewhere?");
2367 // We want to use our display document's device context if possible
2368 nsPresContext* ctx = doc->GetPresContext();
2369 if (ctx) {
2370 mDeviceContext = ctx->DeviceContext();
2371 return NS_OK;
2375 // Create a device context even if we already have one, since our widget
2376 // might have changed.
2377 nsIWidget* widget = nullptr;
2378 if (aContainerView) {
2379 widget = aContainerView->GetNearestWidget(nullptr);
2381 if (!widget) {
2382 widget = mParentWidget;
2384 if (widget) {
2385 widget = widget->GetTopLevelWidget();
2388 mDeviceContext = new nsDeviceContext();
2389 mDeviceContext->Init(widget);
2390 return NS_OK;
2393 // Return the selection for the document. Note that text fields have their
2394 // own selection, which cannot be accessed with this method.
2395 mozilla::dom::Selection* nsDocumentViewer::GetDocumentSelection() {
2396 if (!mPresShell) {
2397 return nullptr;
2400 return mPresShell->GetCurrentSelection(SelectionType::eNormal);
2403 /* ============================================================================
2404 * nsIContentViewerEdit
2405 * ============================================================================
2408 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP nsDocumentViewer::ClearSelection() {
2409 // use nsCopySupport::GetSelectionForCopy() ?
2410 RefPtr<mozilla::dom::Selection> selection = GetDocumentSelection();
2411 if (!selection) {
2412 return NS_ERROR_FAILURE;
2415 ErrorResult rv;
2416 selection->CollapseToStart(rv);
2417 return rv.StealNSResult();
2420 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP nsDocumentViewer::SelectAll() {
2421 // XXX this is a temporary implementation copied from nsWebShell
2422 // for now. I think Document and friends should have some helper
2423 // functions to make this easier.
2425 // use nsCopySupport::GetSelectionForCopy() ?
2426 RefPtr<mozilla::dom::Selection> selection = GetDocumentSelection();
2427 if (!selection) {
2428 return NS_ERROR_FAILURE;
2431 if (!mDocument) {
2432 return NS_ERROR_FAILURE;
2435 nsCOMPtr<nsINode> bodyNode;
2436 if (mDocument->IsHTMLOrXHTML()) {
2437 // XXXbz why not just do GetBody() for all documents, then GetRootElement()
2438 // if GetBody() is null?
2439 bodyNode = mDocument->GetBody();
2440 } else {
2441 bodyNode = mDocument->GetRootElement();
2443 if (!bodyNode) return NS_ERROR_FAILURE;
2445 ErrorResult err;
2446 selection->RemoveAllRanges(err);
2447 if (err.Failed()) {
2448 return err.StealNSResult();
2451 mozilla::dom::Selection::AutoUserInitiated userSelection(selection);
2452 selection->SelectAllChildren(*bodyNode, err);
2453 return err.StealNSResult();
2456 NS_IMETHODIMP nsDocumentViewer::CopySelection() {
2457 RefPtr<PresShell> presShell = mPresShell;
2458 nsCopySupport::FireClipboardEvent(eCopy, nsIClipboard::kGlobalClipboard,
2459 presShell, nullptr);
2460 return NS_OK;
2463 NS_IMETHODIMP nsDocumentViewer::CopyLinkLocation() {
2464 NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_INITIALIZED);
2465 nsCOMPtr<nsINode> node = GetPopupLinkNode();
2466 // make noise if we're not in a link
2467 NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
2469 nsCOMPtr<dom::Element> elm(do_QueryInterface(node));
2470 NS_ENSURE_TRUE(elm, NS_ERROR_FAILURE);
2472 nsAutoString locationText;
2473 nsContentUtils::GetLinkLocation(elm, locationText);
2474 if (locationText.IsEmpty()) return NS_ERROR_FAILURE;
2476 nsresult rv = NS_OK;
2477 nsCOMPtr<nsIClipboardHelper> clipboard(
2478 do_GetService("@mozilla.org/widget/clipboardhelper;1", &rv));
2479 NS_ENSURE_SUCCESS(rv, rv);
2481 // copy the href onto the clipboard
2482 return clipboard->CopyString(locationText);
2485 NS_IMETHODIMP nsDocumentViewer::CopyImage(int32_t aCopyFlags) {
2486 NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_INITIALIZED);
2487 nsCOMPtr<nsIImageLoadingContent> node = GetPopupImageNode();
2488 // make noise if we're not in an image
2489 NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
2491 nsCOMPtr<nsILoadContext> loadContext(mContainer);
2492 return nsCopySupport::ImageCopy(node, loadContext, aCopyFlags);
2495 NS_IMETHODIMP nsDocumentViewer::GetCopyable(bool* aCopyable) {
2496 NS_ENSURE_ARG_POINTER(aCopyable);
2497 *aCopyable = nsCopySupport::CanCopy(mDocument);
2498 return NS_OK;
2501 NS_IMETHODIMP nsDocumentViewer::GetContents(const char* mimeType,
2502 bool selectionOnly,
2503 nsAString& aOutValue) {
2504 aOutValue.Truncate();
2506 NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_INITIALIZED);
2507 NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_INITIALIZED);
2509 // Now we have the selection. Make sure it's nonzero:
2510 RefPtr<Selection> sel;
2511 if (selectionOnly) {
2512 sel = nsCopySupport::GetSelectionForCopy(mDocument);
2513 NS_ENSURE_TRUE(sel, NS_ERROR_FAILURE);
2515 if (NS_WARN_IF(sel->IsCollapsed())) {
2516 return NS_OK;
2520 // call the copy code
2521 return nsCopySupport::GetContents(nsDependentCString(mimeType), 0, sel,
2522 mDocument, aOutValue);
2525 NS_IMETHODIMP nsDocumentViewer::GetCanGetContents(bool* aCanGetContents) {
2526 NS_ENSURE_ARG_POINTER(aCanGetContents);
2527 *aCanGetContents = false;
2528 NS_ENSURE_STATE(mDocument);
2529 *aCanGetContents = nsCopySupport::CanCopy(mDocument);
2530 return NS_OK;
2533 NS_IMETHODIMP nsDocumentViewer::SetCommandNode(nsINode* aNode) {
2534 Document* document = GetDocument();
2535 NS_ENSURE_STATE(document);
2537 nsCOMPtr<nsPIDOMWindowOuter> window(document->GetWindow());
2538 NS_ENSURE_TRUE(window, NS_ERROR_NOT_AVAILABLE);
2540 nsCOMPtr<nsPIWindowRoot> root = window->GetTopWindowRoot();
2541 NS_ENSURE_STATE(root);
2543 root->SetPopupNode(aNode);
2544 return NS_OK;
2547 NS_IMETHODIMP
2548 nsDocumentViewer::GetDeviceFullZoomForTest(float* aDeviceFullZoom) {
2549 NS_ENSURE_ARG_POINTER(aDeviceFullZoom);
2550 nsPresContext* pc = GetPresContext();
2551 *aDeviceFullZoom = pc ? pc->GetDeviceFullZoom() : 1.0;
2552 return NS_OK;
2555 NS_IMETHODIMP
2556 nsDocumentViewer::SetAuthorStyleDisabled(bool aStyleDisabled) {
2557 if (mPresShell) {
2558 mPresShell->SetAuthorStyleDisabled(aStyleDisabled);
2560 return NS_OK;
2563 NS_IMETHODIMP
2564 nsDocumentViewer::GetAuthorStyleDisabled(bool* aStyleDisabled) {
2565 if (mPresShell) {
2566 *aStyleDisabled = mPresShell->GetAuthorStyleDisabled();
2567 } else {
2568 *aStyleDisabled = false;
2570 return NS_OK;
2573 /* [noscript,notxpcom] Encoding getHintCharset (); */
2574 NS_IMETHODIMP_(const Encoding*)
2575 nsDocumentViewer::GetReloadEncodingAndSource(int32_t* aSource) {
2576 *aSource = mReloadEncodingSource;
2577 if (kCharsetUninitialized == mReloadEncodingSource) {
2578 return nullptr;
2580 return mReloadEncoding;
2583 NS_IMETHODIMP_(void)
2584 nsDocumentViewer::SetReloadEncodingAndSource(const Encoding* aEncoding,
2585 int32_t aSource) {
2586 MOZ_ASSERT(
2587 aSource == kCharsetUninitialized ||
2588 (aSource >=
2589 kCharsetFromFinalAutoDetectionWouldHaveBeenUTF8InitialWasASCII &&
2590 aSource <=
2591 kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLDInitialWasASCII) ||
2592 aSource == kCharsetFromFinalUserForcedAutoDetection);
2593 mReloadEncoding = aEncoding;
2594 mReloadEncodingSource = aSource;
2597 NS_IMETHODIMP_(void)
2598 nsDocumentViewer::ForgetReloadEncoding() {
2599 mReloadEncoding = nullptr;
2600 mReloadEncodingSource = kCharsetUninitialized;
2603 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP nsDocumentViewer::GetContentSize(
2604 int32_t aMaxWidth, int32_t aMaxHeight, int32_t aPrefWidth, int32_t* aWidth,
2605 int32_t* aHeight) {
2606 RefPtr<BrowsingContext> bc = mContainer->GetBrowsingContext();
2607 NS_ENSURE_TRUE(bc, NS_ERROR_NOT_AVAILABLE);
2609 // It's only valid to access this from a top frame. Doesn't work from
2610 // sub-frames.
2611 NS_ENSURE_TRUE(bc->IsTop(), NS_ERROR_FAILURE);
2613 // Convert max-width/height and pref-width to app units.
2614 if (aMaxWidth > 0) {
2615 aMaxWidth = CSSPixel::ToAppUnits(aMaxWidth);
2616 } else {
2617 aMaxWidth = NS_UNCONSTRAINEDSIZE;
2619 if (aMaxHeight > 0) {
2620 aMaxHeight = CSSPixel::ToAppUnits(aMaxHeight);
2621 } else {
2622 aMaxHeight = NS_UNCONSTRAINEDSIZE;
2624 if (aPrefWidth > 0) {
2625 aPrefWidth = CSSPixel::ToAppUnits(aPrefWidth);
2626 } else {
2627 aPrefWidth = 0;
2630 RefPtr<PresShell> presShell = GetPresShell();
2631 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
2633 // Flush out all content and style updates. We can't use a resize reflow
2634 // because it won't change some sizes that a style change reflow will.
2635 mDocument->FlushPendingNotifications(FlushType::Layout);
2637 nsIFrame* root = presShell->GetRootFrame();
2638 NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
2640 WritingMode wm = root->GetWritingMode();
2642 nscoord prefISize;
2644 const auto& constraints = presShell->GetWindowSizeConstraints();
2645 aMaxHeight = std::min(aMaxHeight, constraints.mMaxSize.height);
2646 aMaxWidth = std::min(aMaxWidth, constraints.mMaxSize.width);
2648 UniquePtr<gfxContext> rcx(presShell->CreateReferenceRenderingContext());
2649 const nscoord minISize = wm.IsVertical() ? constraints.mMinSize.height
2650 : constraints.mMinSize.width;
2651 const nscoord maxISize = wm.IsVertical() ? aMaxHeight : aMaxWidth;
2652 if (aPrefWidth) {
2653 prefISize = std::max(root->GetMinISize(rcx.get()), aPrefWidth);
2654 } else {
2655 prefISize = root->GetPrefISize(rcx.get());
2657 prefISize = nsPresContext::RoundUpAppUnitsToCSSPixel(
2658 std::max(minISize, std::min(prefISize, maxISize)));
2661 // We should never intentionally get here with this sentinel value, but it's
2662 // possible that a document with huge sizes might inadvertently have a
2663 // prefISize that exactly matches NS_UNCONSTRAINEDSIZE.
2664 // Just bail if that happens.
2665 NS_ENSURE_TRUE(prefISize != NS_UNCONSTRAINEDSIZE, NS_ERROR_FAILURE);
2667 nscoord height = wm.IsVertical() ? prefISize : aMaxHeight;
2668 nscoord width = wm.IsVertical() ? aMaxWidth : prefISize;
2670 presShell->ResizeReflow(width, height, ResizeReflowOptions::BSizeLimit);
2672 RefPtr<nsPresContext> presContext = GetPresContext();
2673 NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
2675 // Protect against bogus returns here
2676 nsRect shellArea = presContext->GetVisibleArea();
2677 NS_ENSURE_TRUE(shellArea.width != NS_UNCONSTRAINEDSIZE &&
2678 shellArea.height != NS_UNCONSTRAINEDSIZE,
2679 NS_ERROR_FAILURE);
2681 // Ceil instead of rounding here, so we can actually guarantee showing all the
2682 // content.
2683 *aWidth = std::ceil(CSSPixel::FromAppUnits(shellArea.width));
2684 *aHeight = std::ceil(CSSPixel::FromAppUnits(shellArea.height));
2686 return NS_OK;
2689 NS_IMPL_ISUPPORTS(nsDocViewerSelectionListener, nsISelectionListener)
2692 * GetPopupNode, GetPopupLinkNode and GetPopupImageNode are helpers
2693 * for the cmd_copyLink / cmd_copyImageLocation / cmd_copyImageContents family
2694 * of commands. The focus controller stores the popup node, these retrieve
2695 * them and munge appropriately. Note that we have to store the popup node
2696 * rather than retrieving it from EventStateManager::GetFocusedContent because
2697 * not all content (images included) can receive focus.
2700 already_AddRefed<nsINode> nsDocumentViewer::GetPopupNode() {
2701 // get the document
2702 Document* document = GetDocument();
2703 NS_ENSURE_TRUE(document, nullptr);
2705 // get the private dom window
2706 nsCOMPtr<nsPIDOMWindowOuter> window(document->GetWindow());
2707 NS_ENSURE_TRUE(window, nullptr);
2708 if (window) {
2709 nsCOMPtr<nsPIWindowRoot> root = window->GetTopWindowRoot();
2710 NS_ENSURE_TRUE(root, nullptr);
2712 // get the popup node
2713 nsCOMPtr<nsINode> node = root->GetPopupNode();
2714 if (!node) {
2715 nsPIDOMWindowOuter* rootWindow = root->GetWindow();
2716 if (rootWindow) {
2717 nsCOMPtr<Document> rootDoc = rootWindow->GetExtantDoc();
2718 if (rootDoc) {
2719 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
2720 if (pm) {
2721 node = pm->GetLastTriggerPopupNode(rootDoc);
2726 return node.forget();
2729 return nullptr;
2732 // GetPopupLinkNode: return popup link node or fail
2733 already_AddRefed<nsINode> nsDocumentViewer::GetPopupLinkNode() {
2734 // find popup node
2735 nsCOMPtr<nsINode> node = GetPopupNode();
2737 // find out if we have a link in our ancestry
2738 while (node) {
2739 if (const auto* element = Element::FromNode(*node)) {
2740 if (element->IsLink()) {
2741 return node.forget();
2745 // get our parent and keep trying...
2746 node = node->GetParentNode();
2749 // if we have no node, fail
2750 return nullptr;
2753 // GetPopupLinkNode: return popup image node or fail
2754 already_AddRefed<nsIImageLoadingContent> nsDocumentViewer::GetPopupImageNode() {
2755 // find popup node
2756 nsCOMPtr<nsINode> node = GetPopupNode();
2757 nsCOMPtr<nsIImageLoadingContent> img = do_QueryInterface(node);
2758 return img.forget();
2762 * XXX dr
2763 * ------
2764 * These two functions -- GetInLink and GetInImage -- are kind of annoying
2765 * in that they only get called from the controller (in
2766 * nsDOMWindowController::IsCommandEnabled). The actual construction of the
2767 * context menus in communicator (nsContextMenu.js) has its own, redundant
2768 * tests. No big deal, but good to keep in mind if we ever clean context
2769 * menus.
2772 NS_IMETHODIMP nsDocumentViewer::GetInLink(bool* aInLink) {
2773 #ifdef DEBUG_dr
2774 printf("dr :: nsDocumentViewer::GetInLink\n");
2775 #endif
2777 NS_ENSURE_ARG_POINTER(aInLink);
2779 // we're not in a link unless i say so
2780 *aInLink = false;
2782 // get the popup link
2783 nsCOMPtr<nsINode> node = GetPopupLinkNode();
2784 if (!node) {
2785 return NS_ERROR_FAILURE;
2788 // if we made it here, we're in a link
2789 *aInLink = true;
2790 return NS_OK;
2793 NS_IMETHODIMP nsDocumentViewer::GetInImage(bool* aInImage) {
2794 #ifdef DEBUG_dr
2795 printf("dr :: nsDocumentViewer::GetInImage\n");
2796 #endif
2798 NS_ENSURE_ARG_POINTER(aInImage);
2800 // we're not in an image unless i say so
2801 *aInImage = false;
2803 // get the popup image
2804 nsCOMPtr<nsIImageLoadingContent> node = GetPopupImageNode();
2805 if (!node) {
2806 return NS_ERROR_FAILURE;
2809 // Make sure there is a URI assigned. This allows <input type="image"> to
2810 // be an image but rejects other <input> types. This matches what
2811 // nsContextMenu.js does.
2812 nsCOMPtr<nsIURI> uri;
2813 node->GetCurrentURI(getter_AddRefs(uri));
2814 if (uri) {
2815 // if we made it here, we're in an image
2816 *aInImage = true;
2819 return NS_OK;
2822 NS_IMETHODIMP nsDocViewerSelectionListener::NotifySelectionChanged(
2823 Document*, Selection*, int16_t aReason, int32_t aAmount) {
2824 if (!mDocViewer) {
2825 return NS_OK;
2828 // get the selection state
2829 RefPtr<mozilla::dom::Selection> selection =
2830 mDocViewer->GetDocumentSelection();
2831 if (!selection) {
2832 return NS_ERROR_FAILURE;
2835 Document* theDoc = mDocViewer->GetDocument();
2836 if (!theDoc) return NS_ERROR_FAILURE;
2838 nsCOMPtr<nsPIDOMWindowOuter> domWindow = theDoc->GetWindow();
2839 if (!domWindow) return NS_ERROR_FAILURE;
2841 bool selectionCollapsed = selection->IsCollapsed();
2842 // We only call UpdateCommands when the selection changes from collapsed to
2843 // non-collapsed or vice versa, however we skip the initializing collapse. We
2844 // might need another update string for simple selection changes, but that
2845 // would be expenseive.
2846 if (mSelectionWasCollapsed != selectionCollapsed) {
2847 domWindow->UpdateCommands(u"select"_ns, selection, aReason);
2848 mSelectionWasCollapsed = selectionCollapsed;
2851 return NS_OK;
2854 // nsDocViewerFocusListener
2855 NS_IMPL_ISUPPORTS(nsDocViewerFocusListener, nsIDOMEventListener)
2857 nsresult nsDocViewerFocusListener::HandleEvent(Event* aEvent) {
2858 NS_ENSURE_STATE(mDocViewer);
2860 RefPtr<PresShell> presShell = mDocViewer->GetPresShell();
2861 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
2863 RefPtr<nsFrameSelection> selection =
2864 presShell->GetLastFocusedFrameSelection();
2865 NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
2866 auto selectionStatus = selection->GetDisplaySelection();
2867 nsAutoString eventType;
2868 aEvent->GetType(eventType);
2869 if (eventType.EqualsLiteral("focus")) {
2870 // If selection was disabled, re-enable it.
2871 if (selectionStatus == nsISelectionController::SELECTION_DISABLED ||
2872 selectionStatus == nsISelectionController::SELECTION_HIDDEN) {
2873 selection->SetDisplaySelection(nsISelectionController::SELECTION_ON);
2874 selection->RepaintSelection(SelectionType::eNormal);
2876 // See EditorBase::FinalizeSelection. This fixes up the case where focus
2877 // left the editor's selection but returned to something else.
2878 if (selection != presShell->ConstFrameSelection()) {
2879 RefPtr<Document> doc = presShell->GetDocument();
2880 const bool selectionMatchesFocus =
2881 selection->GetLimiter() &&
2882 selection->GetLimiter()->GetChromeOnlyAccessSubtreeRootParent() ==
2883 doc->GetUnretargetedFocusedContent();
2884 if (NS_WARN_IF(!selectionMatchesFocus)) {
2885 presShell->FrameSelectionWillLoseFocus(*selection);
2886 presShell->SelectionWillTakeFocus();
2889 } else {
2890 MOZ_ASSERT(eventType.EqualsLiteral("blur"), "Unexpected event type");
2891 // If selection was on, disable it.
2892 if (selectionStatus == nsISelectionController::SELECTION_ON ||
2893 selectionStatus == nsISelectionController::SELECTION_ATTENTION) {
2894 selection->SetDisplaySelection(
2895 nsISelectionController::SELECTION_DISABLED);
2896 selection->RepaintSelection(SelectionType::eNormal);
2900 return NS_OK;
2903 /** ---------------------------------------------------
2904 * From nsIWebBrowserPrint
2907 #ifdef NS_PRINTING
2909 NS_IMETHODIMP
2910 nsDocumentViewer::Print(nsIPrintSettings* aPrintSettings,
2911 RemotePrintJobChild* aRemotePrintJob,
2912 nsIWebProgressListener* aWebProgressListener) {
2913 if (NS_WARN_IF(!mContainer)) {
2914 PR_PL(("Container was destroyed yet we are still trying to use it!"));
2915 return NS_ERROR_FAILURE;
2918 if (NS_WARN_IF(!mDocument) || NS_WARN_IF(!mDeviceContext)) {
2919 PR_PL(("Can't Print without a document and a device context"));
2920 return NS_ERROR_FAILURE;
2923 if (NS_WARN_IF(mPrintJob && mPrintJob->GetIsPrinting())) {
2924 // If we are printing another URL, then exit.
2925 // The reason we check here is because this method can be called while
2926 // another is still in here (the printing dialog is a good example). the
2927 // only time we can print more than one job at a time is the regression
2928 // tests.
2929 nsresult rv = NS_ERROR_NOT_AVAILABLE;
2930 RefPtr<nsPrintJob>(mPrintJob)->FirePrintingErrorEvent(rv);
2931 return rv;
2934 OnDonePrinting();
2936 // Note: mContainer and mDocument are known to be non-null via null-checks
2937 // earlier in this function.
2938 // TODO(dholbert) Do we need to bother with this stack-owned local RefPtr?
2939 // (Is there an edge case where it's needed to keep the nsPrintJob alive?)
2940 RefPtr<nsPrintJob> printJob =
2941 new nsPrintJob(*this, *mContainer, *mDocument,
2942 float(AppUnitsPerCSSInch()) /
2943 float(mDeviceContext->AppUnitsPerDevPixel()));
2944 mPrintJob = printJob;
2946 nsresult rv = printJob->Print(*mDocument, aPrintSettings, aRemotePrintJob,
2947 aWebProgressListener);
2948 if (NS_WARN_IF(NS_FAILED(rv))) {
2949 OnDonePrinting();
2951 return rv;
2954 NS_IMETHODIMP
2955 nsDocumentViewer::PrintPreview(nsIPrintSettings* aPrintSettings,
2956 nsIWebProgressListener* aWebProgressListener,
2957 PrintPreviewResolver&& aCallback) {
2958 # ifdef NS_PRINT_PREVIEW
2959 RefPtr<Document> doc = mDocument.get();
2960 NS_ENSURE_STATE(doc);
2962 if (NS_WARN_IF(GetIsPrinting())) {
2963 return NS_ERROR_FAILURE;
2966 nsCOMPtr<nsIDocShell> docShell(mContainer);
2967 if (NS_WARN_IF(!docShell) || NS_WARN_IF(!mDeviceContext)) {
2968 PR_PL(("Can't Print Preview without device context and docshell"));
2969 return NS_ERROR_FAILURE;
2972 NS_ENSURE_STATE(!GetIsPrinting());
2973 // beforeprint event may have caused ContentViewer to be shutdown.
2974 NS_ENSURE_STATE(mContainer);
2975 NS_ENSURE_STATE(mDeviceContext);
2977 OnDonePrinting();
2979 // Note: mContainer and doc are known to be non-null via null-checks earlier
2980 // in this function.
2981 // TODO(dholbert) Do we need to bother with this stack-owned local RefPtr?
2982 // (Is there an edge case where it's needed to keep the nsPrintJob alive?)
2983 RefPtr<nsPrintJob> printJob =
2984 new nsPrintJob(*this, *mContainer, *doc,
2985 float(AppUnitsPerCSSInch()) /
2986 float(mDeviceContext->AppUnitsPerDevPixel()));
2987 mPrintJob = printJob;
2989 nsresult rv = printJob->PrintPreview(
2990 *doc, aPrintSettings, aWebProgressListener, std::move(aCallback));
2991 if (NS_WARN_IF(NS_FAILED(rv))) {
2992 OnDonePrinting();
2994 return rv;
2995 # else
2996 return NS_ERROR_FAILURE;
2997 # endif // NS_PRINT_PREVIEW
3000 static const nsIFrame* GetTargetPageFrame(int32_t aTargetPageNum,
3001 nsPageSequenceFrame* aSequenceFrame) {
3002 MOZ_ASSERT(aTargetPageNum > 0 &&
3003 aTargetPageNum <=
3004 aSequenceFrame->PrincipalChildList().GetLength());
3005 return aSequenceFrame->PrincipalChildList().FrameAt(aTargetPageNum - 1);
3008 // Calculate the scroll position where the center of |aFrame| is positioned at
3009 // the center of |aScrollable|'s scroll port for the print preview.
3010 // So what we do for that is;
3011 // 1) Calculate the position of the center of |aFrame| in the print preview
3012 // coordinates.
3013 // 2) Reduce the half height of the scroll port from the result of 1.
3014 static nscoord ScrollPositionForFrame(const nsIFrame* aFrame,
3015 nsIScrollableFrame* aScrollable,
3016 float aPreviewScale) {
3017 // Note that even if the computed scroll position is out of the range of
3018 // the scroll port, it gets clamped in nsIScrollableFrame::ScrollTo.
3019 return nscoord(aPreviewScale * aFrame->GetRect().Center().y -
3020 float(aScrollable->GetScrollPortRect().height) / 2.0f);
3023 //----------------------------------------------------------------------
3024 NS_IMETHODIMP
3025 nsDocumentViewer::PrintPreviewScrollToPage(int16_t aType, int32_t aPageNum) {
3026 if (!GetIsPrintPreview() || mPrintJob->GetIsCreatingPrintPreview())
3027 return NS_ERROR_FAILURE;
3029 nsIScrollableFrame* sf = mPresShell->GetRootScrollFrameAsScrollable();
3030 if (!sf) return NS_OK;
3032 auto [seqFrame, sheetCount] = mPrintJob->GetSeqFrameAndCountSheets();
3033 Unused << sheetCount;
3034 if (!seqFrame) {
3035 return NS_ERROR_FAILURE;
3038 float previewScale = seqFrame->GetPrintPreviewScale();
3040 nsPoint dest = sf->GetScrollPosition();
3042 switch (aType) {
3043 case nsIWebBrowserPrint::PRINTPREVIEW_HOME:
3044 dest.y = 0;
3045 break;
3046 case nsIWebBrowserPrint::PRINTPREVIEW_END:
3047 dest.y = sf->GetScrollRange().YMost();
3048 break;
3049 case nsIWebBrowserPrint::PRINTPREVIEW_PREV_PAGE:
3050 case nsIWebBrowserPrint::PRINTPREVIEW_NEXT_PAGE: {
3051 auto [currentFrame, currentSheetNumber] = GetCurrentSheetFrameAndNumber();
3052 Unused << currentSheetNumber;
3053 if (!currentFrame) {
3054 return NS_OK;
3057 const nsIFrame* targetFrame = nullptr;
3058 if (aType == nsIWebBrowserPrint::PRINTPREVIEW_PREV_PAGE) {
3059 targetFrame = currentFrame->GetPrevInFlow();
3060 } else {
3061 targetFrame = currentFrame->GetNextInFlow();
3063 if (!targetFrame) {
3064 return NS_OK;
3067 dest.y = ScrollPositionForFrame(targetFrame, sf, previewScale);
3068 break;
3070 case nsIWebBrowserPrint::PRINTPREVIEW_GOTO_PAGENUM: {
3071 if (aPageNum <= 0 || aPageNum > sheetCount) {
3072 return NS_ERROR_INVALID_ARG;
3075 const nsIFrame* targetFrame = GetTargetPageFrame(aPageNum, seqFrame);
3076 MOZ_ASSERT(targetFrame);
3078 dest.y = ScrollPositionForFrame(targetFrame, sf, previewScale);
3079 break;
3081 default:
3082 return NS_ERROR_INVALID_ARG;
3083 break;
3086 sf->ScrollTo(dest, ScrollMode::Instant);
3088 return NS_OK;
3091 std::tuple<const nsIFrame*, int32_t>
3092 nsDocumentViewer::GetCurrentSheetFrameAndNumber() const {
3093 MOZ_ASSERT(mPrintJob);
3094 MOZ_ASSERT(GetIsPrintPreview() && !mPrintJob->GetIsCreatingPrintPreview());
3096 // in PP mPrtPreview->mPrintObject->mSeqFrame is null
3097 auto [seqFrame, sheetCount] = mPrintJob->GetSeqFrameAndCountSheets();
3098 Unused << sheetCount;
3099 if (!seqFrame) {
3100 return {nullptr, 0};
3103 nsIScrollableFrame* sf = mPresShell->GetRootScrollFrameAsScrollable();
3104 if (!sf) {
3105 // No scrollable contents, returns 1 even if there are multiple sheets.
3106 return {seqFrame->PrincipalChildList().FirstChild(), 1};
3109 nsPoint currentScrollPosition = sf->GetScrollPosition();
3110 float halfwayPoint =
3111 currentScrollPosition.y + float(sf->GetScrollPortRect().height) / 2.0f;
3112 float lastDistanceFromHalfwayPoint = std::numeric_limits<float>::max();
3113 int32_t sheetNumber = 0;
3114 const nsIFrame* currentSheet = nullptr;
3115 float previewScale = seqFrame->GetPrintPreviewScale();
3116 for (const nsIFrame* sheetFrame : seqFrame->PrincipalChildList()) {
3117 nsRect sheetRect = sheetFrame->GetRect();
3118 sheetNumber++;
3119 currentSheet = sheetFrame;
3121 float bottomOfSheet = sheetRect.YMost() * previewScale;
3122 if (bottomOfSheet < halfwayPoint) {
3123 // If the bottom of the sheet is not yet over the halfway point, iterate
3124 // the next frame to see if the next frame is over the halfway point and
3125 // compare the distance from the halfway point.
3126 lastDistanceFromHalfwayPoint = halfwayPoint - bottomOfSheet;
3127 continue;
3130 float topOfSheet = sheetRect.Y() * previewScale;
3131 if (topOfSheet <= halfwayPoint) {
3132 // If the top of the sheet is not yet over the halfway point or on the
3133 // point, it's the current sheet.
3134 break;
3137 // Now the sheet rect is completely over the halfway point, compare the
3138 // distances from the halfway point.
3139 if ((topOfSheet - halfwayPoint) >= lastDistanceFromHalfwayPoint) {
3140 // If the previous sheet distance is less than or equal to the current
3141 // sheet distance, choose the previous one as the current.
3142 sheetNumber--;
3143 MOZ_ASSERT(sheetNumber > 0);
3144 currentSheet = currentSheet->GetPrevInFlow();
3145 MOZ_ASSERT(currentSheet);
3147 break;
3150 MOZ_ASSERT(sheetNumber <= sheetCount);
3151 return {currentSheet, sheetNumber};
3154 // XXXdholbert As noted in nsIWebBrowserPrint.idl, this API (the IDL attr
3155 // 'printPreviewCurrentPageNumber') is misnamed and needs s/Page/Sheet/. See
3156 // bug 1669762.
3157 NS_IMETHODIMP
3158 nsDocumentViewer::GetPrintPreviewCurrentPageNumber(int32_t* aNumber) {
3159 NS_ENSURE_ARG_POINTER(aNumber);
3160 NS_ENSURE_TRUE(mPrintJob, NS_ERROR_FAILURE);
3161 if (!GetIsPrintPreview() || mPrintJob->GetIsCreatingPrintPreview()) {
3162 return NS_ERROR_FAILURE;
3165 auto [currentFrame, currentSheetNumber] = GetCurrentSheetFrameAndNumber();
3166 Unused << currentFrame;
3167 if (!currentSheetNumber) {
3168 return NS_ERROR_FAILURE;
3171 *aNumber = currentSheetNumber;
3173 return NS_OK;
3176 // XXX This always returns false for subdocuments
3177 NS_IMETHODIMP
3178 nsDocumentViewer::GetDoingPrint(bool* aDoingPrint) {
3179 NS_ENSURE_ARG_POINTER(aDoingPrint);
3181 // XXX shouldn't this be GetDoingPrint() ?
3182 *aDoingPrint = mPrintJob ? mPrintJob->CreatedForPrintPreview() : false;
3183 return NS_OK;
3186 // XXX This always returns false for subdocuments
3187 NS_IMETHODIMP
3188 nsDocumentViewer::GetDoingPrintPreview(bool* aDoingPrintPreview) {
3189 NS_ENSURE_ARG_POINTER(aDoingPrintPreview);
3191 *aDoingPrintPreview = mPrintJob ? mPrintJob->CreatedForPrintPreview() : false;
3192 return NS_OK;
3195 NS_IMETHODIMP
3196 nsDocumentViewer::ExitPrintPreview() {
3197 NS_ENSURE_TRUE(mPrintJob, NS_ERROR_FAILURE);
3199 if (GetIsPrinting()) {
3200 // Block exiting the print preview window if we're in the middle of an
3201 // actual print.
3202 return NS_ERROR_FAILURE;
3205 if (!GetIsPrintPreview()) {
3206 NS_ERROR("Wow, we should never get here!");
3207 return NS_OK;
3210 # ifdef NS_PRINT_PREVIEW
3211 mPrintJob->Destroy();
3212 mPrintJob = nullptr;
3214 // Since the print preview implementation discards the window that was used
3215 // to show the print preview, we skip certain cleanup that we would otherwise
3216 // want to do. Specifically, we do not call `SetIsPrintPreview(false)` to
3217 // unblock navigation, we do not call `SetOverrideDPPX` to reset the
3218 // devicePixelRatio, and we do not call `Show` to make such changes take
3219 // affect.
3220 # endif // NS_PRINT_PREVIEW
3222 return NS_OK;
3225 NS_IMETHODIMP
3226 nsDocumentViewer::GetRawNumPages(int32_t* aRawNumPages) {
3227 NS_ENSURE_ARG_POINTER(aRawNumPages);
3228 NS_ENSURE_TRUE(mPrintJob, NS_ERROR_FAILURE);
3230 *aRawNumPages = mPrintJob->GetRawNumPages();
3231 return *aRawNumPages > 0 ? NS_OK : NS_ERROR_FAILURE;
3234 // XXXdholbert As noted in nsIWebBrowserPrint.idl, this API (the IDL attr
3235 // 'printPreviewNumPages') is misnamed and needs s/Page/Sheet/.
3236 // See bug 1669762.
3237 NS_IMETHODIMP
3238 nsDocumentViewer::GetPrintPreviewNumPages(int32_t* aPrintPreviewNumPages) {
3239 NS_ENSURE_ARG_POINTER(aPrintPreviewNumPages);
3240 NS_ENSURE_TRUE(mPrintJob, NS_ERROR_FAILURE);
3241 *aPrintPreviewNumPages = mPrintJob->GetPrintPreviewNumSheets();
3242 return *aPrintPreviewNumPages > 0 ? NS_OK : NS_ERROR_FAILURE;
3245 //----------------------------------------------------------------------------------
3246 // Printing/Print Preview Helpers
3247 //----------------------------------------------------------------------------------
3249 //----------------------------------------------------------------------------------
3250 // Walks the document tree and tells each DocShell whether Printing/PP is
3251 // happening
3252 #endif // NS_PRINTING
3254 bool nsDocumentViewer::ShouldAttachToTopLevel() {
3255 if (!mParentWidget) {
3256 return false;
3259 if (!mContainer) {
3260 return false;
3263 // We always attach when using puppet widgets
3264 if (nsIWidget::UsePuppetWidgets()) {
3265 return true;
3268 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) || \
3269 defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_UIKIT)
3270 if (!mPresContext) {
3271 return false;
3274 // On windows, in the parent process we also attach, but just to
3275 // chrome items
3276 auto winType = mParentWidget->GetWindowType();
3277 if ((winType == widget::WindowType::TopLevel ||
3278 winType == widget::WindowType::Dialog ||
3279 winType == widget::WindowType::Invisible) &&
3280 mPresContext->IsChrome()) {
3281 return true;
3283 #endif
3285 return false;
3288 //------------------------------------------------------------
3289 // XXX this always returns false for subdocuments
3290 bool nsDocumentViewer::GetIsPrinting() const {
3291 #ifdef NS_PRINTING
3292 if (mPrintJob) {
3293 return mPrintJob->GetIsPrinting();
3295 #endif
3296 return false;
3299 //------------------------------------------------------------
3300 // The PrintJob holds the current value
3301 // this called from inside the DocViewer.
3302 // XXX it always returns false for subdocuments
3303 bool nsDocumentViewer::GetIsPrintPreview() const {
3304 #ifdef NS_PRINTING
3305 return mPrintJob && mPrintJob->CreatedForPrintPreview();
3306 #else
3307 return false;
3308 #endif
3311 //------------------------------------------------------------
3312 // Notification from the PrintJob of the current PP status
3313 void nsDocumentViewer::SetIsPrintPreview(bool aIsPrintPreview) {
3314 // Protect against pres shell destruction running scripts.
3315 nsAutoScriptBlocker scriptBlocker;
3317 if (!aIsPrintPreview) {
3318 InvalidatePotentialSubDocDisplayItem();
3319 if (mPresShell) {
3320 DestroyPresShell();
3322 mWindow = nullptr;
3323 mViewManager = nullptr;
3324 mPresContext = nullptr;
3325 mPresShell = nullptr;
3329 //----------------------------------------------------------------------------------
3330 // nsIDocumentViewerPrint IFace
3331 //----------------------------------------------------------------------------------
3333 //------------------------------------------------------------
3334 void nsDocumentViewer::IncrementDestroyBlockedCount() {
3335 ++mDestroyBlockedCount;
3338 void nsDocumentViewer::DecrementDestroyBlockedCount() {
3339 --mDestroyBlockedCount;
3342 //------------------------------------------------------------
3343 // This called ONLY when printing has completed and the DV
3344 // is being notified that it should get rid of the nsPrintJob.
3346 // BUT, if we are in Print Preview then we want to ignore the
3347 // notification (we do not get rid of the nsPrintJob)
3349 // One small caveat:
3350 // This IS called from two places in this module for cleaning
3351 // up when an error occurred during the start up printing
3352 // and print preview
3354 void nsDocumentViewer::OnDonePrinting() {
3355 #if defined(NS_PRINTING) && defined(NS_PRINT_PREVIEW)
3356 // If Destroy() has been called during calling nsPrintJob::Print() or
3357 // nsPrintJob::PrintPreview(), mPrintJob is already nullptr here.
3358 // So, the following clean up does nothing in such case.
3359 // (Do we need some of this for that case?)
3360 if (mPrintJob) {
3361 RefPtr<nsPrintJob> printJob = std::move(mPrintJob);
3362 if (GetIsPrintPreview()) {
3363 printJob->DestroyPrintingData();
3364 } else {
3365 printJob->Destroy();
3368 // We are done printing, now clean up.
3370 // For non-print-preview jobs, we are actually responsible for cleaning up
3371 // our whole <browser> or window (see the OPEN_PRINT_BROWSER code), so gotta
3372 // run window.close(), which will take care of this.
3374 // For print preview jobs the front-end code is responsible for cleaning the
3375 // UI.
3376 if (!printJob->CreatedForPrintPreview()) {
3377 if (mContainer) {
3378 if (nsCOMPtr<nsPIDOMWindowOuter> win = mContainer->GetWindow()) {
3379 win->Close();
3382 } else if (mClosingWhilePrinting) {
3383 if (mDocument) {
3384 mDocument->Destroy();
3385 mDocument = nullptr;
3387 mClosingWhilePrinting = false;
3390 #endif // NS_PRINTING && NS_PRINT_PREVIEW
3393 NS_IMETHODIMP nsDocumentViewer::SetPrintSettingsForSubdocument(
3394 nsIPrintSettings* aPrintSettings, RemotePrintJobChild* aRemotePrintJob) {
3395 #ifdef NS_PRINTING
3397 nsAutoScriptBlocker scriptBlocker;
3399 if (mPresShell) {
3400 DestroyPresShell();
3403 if (mPresContext) {
3404 DestroyPresContext();
3407 MOZ_ASSERT(!mPresContext);
3408 MOZ_ASSERT(!mPresShell);
3410 if (MOZ_UNLIKELY(!mDocument)) {
3411 return NS_ERROR_NOT_AVAILABLE;
3414 RefPtr<nsDeviceContextSpecProxy> devspec =
3415 new nsDeviceContextSpecProxy(aRemotePrintJob);
3416 nsresult rv = devspec->Init(aPrintSettings, /* aIsPrintPreview = */ true);
3417 NS_ENSURE_SUCCESS(rv, rv);
3419 mDeviceContext = new nsDeviceContext();
3420 rv = mDeviceContext->InitForPrinting(devspec);
3422 NS_ENSURE_SUCCESS(rv, rv);
3424 mPresContext = CreatePresContext(
3425 mDocument, nsPresContext::eContext_PrintPreview, FindContainerView());
3426 mPresContext->SetPrintSettings(aPrintSettings);
3427 rv = mPresContext->Init(mDeviceContext);
3428 NS_ENSURE_SUCCESS(rv, rv);
3430 rv = MakeWindow(nsSize(mPresContext->DevPixelsToAppUnits(mBounds.width),
3431 mPresContext->DevPixelsToAppUnits(mBounds.height)),
3432 FindContainerView());
3433 NS_ENSURE_SUCCESS(rv, rv);
3435 MOZ_TRY(InitPresentationStuff(true));
3438 RefPtr<PresShell> shell = mPresShell;
3439 shell->FlushPendingNotifications(FlushType::Layout);
3440 #endif
3441 return NS_OK;
3444 NS_IMETHODIMP nsDocumentViewer::SetPageModeForTesting(
3445 bool aPageMode, nsIPrintSettings* aPrintSettings) {
3446 // XXX Page mode is only partially working; it's currently used for
3447 // reftests that require a paginated context
3448 mIsPageMode = aPageMode;
3450 // The DestroyPresShell call requires a script blocker, since the
3451 // PresShell::Destroy call it does can cause scripts to run, which could
3452 // re-entrantly call methods on the nsDocumentViewer.
3453 nsAutoScriptBlocker scriptBlocker;
3455 if (mPresShell) {
3456 DestroyPresShell();
3459 if (mPresContext) {
3460 DestroyPresContext();
3463 mViewManager = nullptr;
3464 mWindow = nullptr;
3466 NS_ENSURE_STATE(mDocument);
3467 if (aPageMode) {
3468 mPresContext = CreatePresContext(
3469 mDocument, nsPresContext::eContext_PageLayout, FindContainerView());
3470 NS_ENSURE_TRUE(mPresContext, NS_ERROR_OUT_OF_MEMORY);
3471 mPresContext->SetPaginatedScrolling(true);
3472 mPresContext->SetPrintSettings(aPrintSettings);
3473 nsresult rv = mPresContext->Init(mDeviceContext);
3474 NS_ENSURE_SUCCESS(rv, rv);
3476 NS_ENSURE_SUCCESS(InitInternal(mParentWidget, nullptr, nullptr, mBounds, true,
3477 false, false),
3478 NS_ERROR_FAILURE);
3480 Show();
3481 return NS_OK;
3484 NS_IMETHODIMP
3485 nsDocumentViewer::GetHistoryEntry(nsISHEntry** aHistoryEntry) {
3486 NS_IF_ADDREF(*aHistoryEntry = mSHEntry);
3487 return NS_OK;
3490 NS_IMETHODIMP
3491 nsDocumentViewer::GetIsTabModalPromptAllowed(bool* aAllowed) {
3492 *aAllowed = !mHidden;
3493 return NS_OK;
3496 NS_IMETHODIMP
3497 nsDocumentViewer::GetIsHidden(bool* aHidden) {
3498 *aHidden = mHidden;
3499 return NS_OK;
3502 NS_IMETHODIMP
3503 nsDocumentViewer::SetIsHidden(bool aHidden) {
3504 mHidden = aHidden;
3505 return NS_OK;
3508 void nsDocumentViewer::DestroyPresShell() {
3509 // We assert this because destroying the pres shell could otherwise cause
3510 // re-entrancy into nsDocumentViewer methods, and all callers of
3511 // DestroyPresShell need to do other cleanup work afterwards before it
3512 // is safe for those re-entrant method calls to be made.
3513 MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(),
3514 "DestroyPresShell must only be called when scripts are blocked");
3516 // Break circular reference (or something)
3517 mPresShell->EndObservingDocument();
3519 RefPtr<mozilla::dom::Selection> selection = GetDocumentSelection();
3520 if (selection && mSelectionListener)
3521 selection->RemoveSelectionListener(mSelectionListener);
3523 mPresShell->Destroy();
3524 mPresShell = nullptr;
3527 void nsDocumentViewer::InvalidatePotentialSubDocDisplayItem() {
3528 if (mViewManager) {
3529 if (nsView* rootView = mViewManager->GetRootView()) {
3530 if (nsView* rootViewParent = rootView->GetParent()) {
3531 if (nsView* subdocview = rootViewParent->GetParent()) {
3532 if (nsIFrame* f = subdocview->GetFrame()) {
3533 if (nsSubDocumentFrame* s = do_QueryFrame(f)) {
3534 s->MarkNeedsDisplayItemRebuild();
3543 void nsDocumentViewer::DestroyPresContext() {
3544 InvalidatePotentialSubDocDisplayItem();
3545 mPresContext = nullptr;
3548 void nsDocumentViewer::SetPrintPreviewPresentation(nsViewManager* aViewManager,
3549 nsPresContext* aPresContext,
3550 PresShell* aPresShell) {
3551 // Protect against pres shell destruction running scripts and re-entrantly
3552 // creating a new presentation.
3553 nsAutoScriptBlocker scriptBlocker;
3555 if (mPresShell) {
3556 DestroyPresShell();
3559 mWindow = nullptr;
3560 mViewManager = aViewManager;
3561 mPresContext = aPresContext;
3562 mPresShell = aPresShell;
3564 if (ShouldAttachToTopLevel()) {
3565 DetachFromTopLevelWidget();
3566 nsView* rootView = mViewManager->GetRootView();
3567 rootView->AttachToTopLevelWidget(mParentWidget);
3568 mAttachedToParent = true;
3572 // Fires the "document-shown" event so that interested parties are aware of it.
3573 NS_IMETHODIMP
3574 nsDocumentShownDispatcher::Run() {
3575 nsCOMPtr<nsIObserverService> observerService =
3576 mozilla::services::GetObserverService();
3577 if (observerService) {
3578 observerService->NotifyObservers(ToSupports(mDocument), "document-shown",
3579 nullptr);
3581 return NS_OK;