1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 /* container for a document and its presentation */
8 
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 "nsPresContext.h"
34 #include "nsIFrame.h"
35 #include "nsIWritablePropertyBag2.h"
36 #include "nsSubDocumentFrame.h"
37 #include "nsGenericHTMLElement.h"
38 #include "nsStubMutationObserver.h"
39 
40 #include "nsISelectionListener.h"
41 #include "mozilla/dom/Selection.h"
42 #include "nsContentUtils.h"
43 #ifdef ACCESSIBILITY
44 #  include "mozilla/a11y/DocAccessible.h"
45 #endif
46 #include "mozilla/BasicEvents.h"
47 #include "mozilla/Encoding.h"
48 #include "mozilla/ErrorResult.h"
49 #include "mozilla/Preferences.h"
50 #include "mozilla/SpinEventLoopUntil.h"
51 #include "mozilla/WeakPtr.h"
52 #include "mozilla/StaticPrefs_dom.h"
53 #include "mozilla/StaticPrefs_javascript.h"
54 #include "mozilla/StaticPrefs_fission.h"
55 #include "mozilla/StaticPrefs_print.h"
56 #include "mozilla/StyleSheet.h"
57 #include "mozilla/StyleSheetInlines.h"
58 
59 #include "nsViewManager.h"
60 #include "nsView.h"
61 
62 #include "nsPageSequenceFrame.h"
63 #include "nsNetUtil.h"
64 #include "nsIContentViewerEdit.h"
65 #include "mozilla/css/Loader.h"
66 #include "nsIInterfaceRequestor.h"
67 #include "nsIInterfaceRequestorUtils.h"
68 #include "nsDocShell.h"
69 #include "nsIBaseWindow.h"
70 #include "nsILayoutHistoryState.h"
71 #include "nsIScreen.h"
72 #include "nsCharsetSource.h"
73 #include "mozilla/ReflowInput.h"
74 #include "nsIImageLoadingContent.h"
75 #include "nsCopySupport.h"
76 #include "nsXULPopupManager.h"
77 
78 #include "nsIClipboardHelper.h"
79 
80 #include "nsPIDOMWindow.h"
81 #include "nsGlobalWindow.h"
82 #include "nsDOMNavigationTiming.h"
83 #include "nsPIWindowRoot.h"
84 #include "nsJSEnvironment.h"
85 #include "nsFocusManager.h"
86 
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"
96 
97 #include "mozilla/DocLoadingTimelineMarker.h"
98 
99 //--------------------------
100 // Printing Include
101 //---------------------------
102 #ifdef NS_PRINTING
103 
104 #  include "nsIWebBrowserPrint.h"
105 
106 #  include "nsPrintJob.h"
107 #  include "nsDeviceContextSpecProxy.h"
108 
109 // Print Options
110 #  include "nsIPrintSettings.h"
111 #  include "nsIPrintSettingsService.h"
112 #  include "nsISimpleEnumerator.h"
113 
114 #endif  // NS_PRINTING
115 
116 // focus
117 #include "nsIDOMEventListener.h"
118 #include "nsISelectionController.h"
119 
120 #include "mozilla/EventDispatcher.h"
121 #include "nsISHEntry.h"
122 #include "nsISHistory.h"
123 #include "nsIWebNavigation.h"
124 #include "mozilla/dom/XMLHttpRequestMainThread.h"
125 
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"
134 
135 namespace mozilla {
136 namespace dom {
137 class PrintPreviewResultInfo;
138 }  // namespace dom
139 }  // namespace mozilla
140 
141 using namespace mozilla;
142 using namespace mozilla::dom;
143 
144 using PrintPreviewResolver =
145     std::function<void(const mozilla::dom::PrintPreviewResultInfo&)>;
146 
147 //-----------------------------------------------------
148 // LOGGING
149 #include "LayoutLogging.h"
150 #include "mozilla/Logging.h"
151 
152 extern mozilla::LazyLogModule gPageCacheLog;
153 
154 #ifdef NS_PRINTING
155 mozilla::LazyLogModule gPrintingLog("printing");
156 
157 #  define PR_PL(_p1) MOZ_LOG(gPrintingLog, mozilla::LogLevel::Debug, _p1);
158 #endif  // NS_PRINTING
159 
160 #define PRT_YESNO(_p) ((_p) ? "YES" : "NO")
161 //-----------------------------------------------------
162 
163 class nsDocumentViewer;
164 
165 // a small delegate class used to avoid circular references
166 
167 class nsDocViewerSelectionListener final : public nsISelectionListener {
168  public:
169   // nsISupports interface...
170   NS_DECL_ISUPPORTS
171 
172   // nsISelectionListerner interface
173   NS_DECL_NSISELECTIONLISTENER
174 
nsDocViewerSelectionListener(nsDocumentViewer * aDocViewer)175   explicit nsDocViewerSelectionListener(nsDocumentViewer* aDocViewer)
176       : mDocViewer(aDocViewer), mSelectionWasCollapsed(true) {}
177 
Disconnect()178   void Disconnect() { mDocViewer = nullptr; }
179 
180  protected:
181   virtual ~nsDocViewerSelectionListener() = default;
182 
183   nsDocumentViewer* mDocViewer;
184   bool mSelectionWasCollapsed;
185 };
186 
187 /** editor Implementation of the FocusListener interface */
188 class nsDocViewerFocusListener final : public nsIDOMEventListener {
189  public:
nsDocViewerFocusListener(nsDocumentViewer * aDocViewer)190   explicit nsDocViewerFocusListener(nsDocumentViewer* aDocViewer)
191       : mDocViewer(aDocViewer) {}
192 
193   NS_DECL_ISUPPORTS
194   NS_DECL_NSIDOMEVENTLISTENER
195 
Disconnect()196   void Disconnect() { mDocViewer = nullptr; }
197 
198  protected:
199   virtual ~nsDocViewerFocusListener() = default;
200 
201   nsDocumentViewer* mDocViewer;
202 };
203 
204 namespace viewer_detail {
205 
206 /**
207  * Mutation observer for use until we hand ourselves over to our SHEntry.
208  */
209 class BFCachePreventionObserver final : public nsStubMutationObserver {
210  public:
BFCachePreventionObserver(Document * aDocument)211   explicit BFCachePreventionObserver(Document* aDocument)
212       : mDocument(aDocument) {}
213 
214   NS_DECL_ISUPPORTS
215 
216   NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
217   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
218   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
219   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
220   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
221   NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
222 
223   // Stop observing the document.
224   void Disconnect();
225 
226  private:
227   ~BFCachePreventionObserver() = default;
228 
229   // Helper for the work that needs to happen when mutations happen.
230   void MutationHappened();
231 
232   Document* mDocument;  // Weak; we get notified if it dies
233 };
234 
NS_IMPL_ISUPPORTS(BFCachePreventionObserver,nsIMutationObserver)235 NS_IMPL_ISUPPORTS(BFCachePreventionObserver, nsIMutationObserver)
236 
237 void BFCachePreventionObserver::CharacterDataChanged(
238     nsIContent* aContent, const CharacterDataChangeInfo&) {
239   if (aContent->IsInNativeAnonymousSubtree()) {
240     return;
241   }
242   MutationHappened();
243 }
244 
AttributeChanged(Element * aElement,int32_t aNameSpaceID,nsAtom * aAttribute,int32_t aModType,const nsAttrValue * aOldValue)245 void BFCachePreventionObserver::AttributeChanged(Element* aElement,
246                                                  int32_t aNameSpaceID,
247                                                  nsAtom* aAttribute,
248                                                  int32_t aModType,
249                                                  const nsAttrValue* aOldValue) {
250   if (aElement->IsInNativeAnonymousSubtree()) {
251     return;
252   }
253   MutationHappened();
254 }
255 
ContentAppended(nsIContent * aFirstNewContent)256 void BFCachePreventionObserver::ContentAppended(nsIContent* aFirstNewContent) {
257   if (aFirstNewContent->IsInNativeAnonymousSubtree()) {
258     return;
259   }
260   MutationHappened();
261 }
262 
ContentInserted(nsIContent * aChild)263 void BFCachePreventionObserver::ContentInserted(nsIContent* aChild) {
264   if (aChild->IsInNativeAnonymousSubtree()) {
265     return;
266   }
267   MutationHappened();
268 }
269 
ContentRemoved(nsIContent * aChild,nsIContent * aPreviousSibling)270 void BFCachePreventionObserver::ContentRemoved(nsIContent* aChild,
271                                                nsIContent* aPreviousSibling) {
272   if (aChild->IsInNativeAnonymousSubtree()) {
273     return;
274   }
275   MutationHappened();
276 }
277 
NodeWillBeDestroyed(const nsINode * aNode)278 void BFCachePreventionObserver::NodeWillBeDestroyed(const nsINode* aNode) {
279   mDocument = nullptr;
280 }
281 
Disconnect()282 void BFCachePreventionObserver::Disconnect() {
283   if (mDocument) {
284     mDocument->RemoveMutationObserver(this);
285     // It will no longer tell us when it goes away, so make sure we're
286     // not holding a dangling ref.
287     mDocument = nullptr;
288   }
289 }
290 
MutationHappened()291 void BFCachePreventionObserver::MutationHappened() {
292   MOZ_ASSERT(
293       mDocument,
294       "How can we not have a document but be getting notified for mutations?");
295   mDocument->DisallowBFCaching();
296   Disconnect();
297 }
298 
299 }  // namespace viewer_detail
300 
301 using viewer_detail::BFCachePreventionObserver;
302 
303 //-------------------------------------------------------------
304 class nsDocumentViewer final : public nsIContentViewer,
305                                public nsIContentViewerEdit,
306                                public nsIDocumentViewerPrint
307 #ifdef NS_PRINTING
308     ,
309                                public nsIWebBrowserPrint
310 #endif
311 
312 {
313   friend class nsDocViewerSelectionListener;
314   friend class nsPagePrintTimer;
315   friend class nsPrintJob;
316 
317  public:
318   nsDocumentViewer();
319 
320   // nsISupports interface...
321   NS_DECL_ISUPPORTS
322 
323   // nsIContentViewer interface...
324   NS_DECL_NSICONTENTVIEWER
325 
326   // nsIContentViewerEdit
327   NS_DECL_NSICONTENTVIEWEREDIT
328 
329 #ifdef NS_PRINTING
330   // nsIWebBrowserPrint
331   NS_DECL_NSIWEBBROWSERPRINT
332 #endif
333 
334   // nsIDocumentViewerPrint Printing Methods
335   NS_DECL_NSIDOCUMENTVIEWERPRINT
336 
337  protected:
338   virtual ~nsDocumentViewer();
339 
340  private:
341   /**
342    * Creates a view manager, root view, and widget for the root view, setting
343    * mViewManager and mWindow.
344    * @param aSize the initial size in appunits
345    * @param aContainerView the container view to hook our root view up
346    * to as a child, or null if this will be the root view manager
347    */
348   nsresult MakeWindow(const nsSize& aSize, nsView* aContainerView);
349 
350   /**
351    * Create our device context
352    */
353   nsresult CreateDeviceContext(nsView* aContainerView);
354 
355   /**
356    * If aDoCreation is true, this creates the device context, creates a
357    * prescontext if necessary, and calls MakeWindow.
358    *
359    * If aForceSetNewDocument is false, then SetNewDocument won't be
360    * called if the window's current document is already mDocument.
361    */
362   nsresult InitInternal(nsIWidget* aParentWidget, nsISupports* aState,
363                         mozilla::dom::WindowGlobalChild* aActor,
364                         const nsIntRect& aBounds, bool aDoCreation,
365                         bool aNeedMakeCX = true,
366                         bool aForceSetNewDocument = true);
367   /**
368    * @param aDoInitialReflow set to true if you want to kick off the initial
369    * reflow
370    */
371   MOZ_CAN_RUN_SCRIPT_BOUNDARY
372   nsresult InitPresentationStuff(bool aDoInitialReflow);
373 
374   already_AddRefed<nsINode> GetPopupNode();
375   already_AddRefed<nsINode> GetPopupLinkNode();
376   already_AddRefed<nsIImageLoadingContent> GetPopupImageNode();
377 
378   MOZ_CAN_RUN_SCRIPT_BOUNDARY
379   nsresult GetContentSizeInternal(int32_t* aWidth, int32_t* aHeight,
380                                   nscoord aMaxWidth, nscoord aMaxHeight);
381 
382   void PrepareToStartLoad(void);
383 
384   nsresult SyncParentSubDocMap();
385 
386   void RemoveFocusListener();
387   void ReinitializeFocusListener();
388 
389   mozilla::dom::Selection* GetDocumentSelection();
390 
391   void DestroyPresShell();
392   void DestroyPresContext();
393 
394   void InvalidatePotentialSubDocDisplayItem();
395 
396   // Whether we should attach to the top level widget. This is true if we
397   // are sharing/recycling a single base widget and not creating multiple
398   // child widgets.
399   bool ShouldAttachToTopLevel();
400 
401   std::tuple<const nsIFrame*, int32_t> GetCurrentSheetFrameAndNumber() const;
402 
403  protected:
404   // Returns the current viewmanager.  Might be null.
405   nsViewManager* GetViewManager();
406 
407   void DetachFromTopLevelWidget();
408 
409   // IMPORTANT: The ownership implicit in the following member
410   // variables has been explicitly checked and set using nsCOMPtr
411   // for owning pointers and raw COM interface pointers for weak
412   // (ie, non owning) references. If you add any members to this
413   // class, please make the ownership explicit (pinkerton, scc).
414 
415   WeakPtr<nsDocShell> mContainer;          // it owns me!
416   RefPtr<nsDeviceContext> mDeviceContext;  // We create and own this baby
417 
418   // the following six items are explicitly in this order
419   // so they will be destroyed in the reverse order (pinkerton, scc)
420   nsCOMPtr<Document> mDocument;
421   nsCOMPtr<nsIWidget> mWindow;  // may be null
422   RefPtr<nsViewManager> mViewManager;
423   RefPtr<nsPresContext> mPresContext;
424   RefPtr<PresShell> mPresShell;
425 
426   RefPtr<nsDocViewerSelectionListener> mSelectionListener;
427   RefPtr<nsDocViewerFocusListener> mFocusListener;
428 
429   nsCOMPtr<nsIContentViewer> mPreviousViewer;
430   nsCOMPtr<nsISHEntry> mSHEntry;
431   // Observer that will prevent bfcaching if it gets notified.  This
432   // is non-null precisely when mSHEntry is non-null.
433   RefPtr<BFCachePreventionObserver> mBFCachePreventionObserver;
434 
435   nsIWidget* mParentWidget;  // purposely won't be ref counted.  May be null
436   bool mAttachedToParent;    // view is attached to the parent widget
437 
438   nsIntRect mBounds;
439 
440   int16_t mNumURLStarts;
441   int16_t mDestroyBlockedCount;
442 
443   unsigned mStopped : 1;
444   unsigned mLoaded : 1;
445   unsigned mDeferredWindowClose : 1;
446   // document management data
447   //   these items are specific to markup documents (html and xml)
448   //   may consider splitting these out into a subclass
449   unsigned mIsSticky : 1;
450   unsigned mInPermitUnload : 1;
451   unsigned mInPermitUnloadPrompt : 1;
452 
453 #ifdef NS_PRINTING
454   unsigned mClosingWhilePrinting : 1;
455 
456 #  if NS_PRINT_PREVIEW
457   RefPtr<nsPrintJob> mPrintJob;
458 #  endif  // NS_PRINT_PREVIEW
459 
460 #endif  // NS_PRINTING
461 
462   /* character set member data */
463   int32_t mReloadEncodingSource;
464   const Encoding* mReloadEncoding;
465 
466   bool mIsPageMode;
467   bool mInitializedForPrintPreview;
468   bool mHidden;
469 };
470 
471 class nsDocumentShownDispatcher : public Runnable {
472  public:
nsDocumentShownDispatcher(nsCOMPtr<Document> aDocument)473   explicit nsDocumentShownDispatcher(nsCOMPtr<Document> aDocument)
474       : Runnable("nsDocumentShownDispatcher"), mDocument(aDocument) {}
475 
476   NS_IMETHOD Run() override;
477 
478  private:
479   nsCOMPtr<Document> mDocument;
480 };
481 
482 //------------------------------------------------------------------
483 // nsDocumentViewer
484 //------------------------------------------------------------------
485 
486 //------------------------------------------------------------------
NS_NewContentViewer()487 already_AddRefed<nsIContentViewer> NS_NewContentViewer() {
488   RefPtr<nsDocumentViewer> viewer = new nsDocumentViewer();
489   return viewer.forget();
490 }
491 
PrepareToStartLoad()492 void nsDocumentViewer::PrepareToStartLoad() {
493   MOZ_DIAGNOSTIC_ASSERT(!GetIsPrintPreview(),
494                         "Print preview tab should never navigate");
495 
496   mStopped = false;
497   mLoaded = false;
498   mAttachedToParent = false;
499   mDeferredWindowClose = false;
500 
501 #ifdef NS_PRINTING
502   mClosingWhilePrinting = false;
503 
504   // Make sure we have destroyed it and cleared the data member
505   if (mPrintJob) {
506     mPrintJob->Destroy();
507     mPrintJob = nullptr;
508   }
509 
510 #endif  // NS_PRINTING
511 }
512 
nsDocumentViewer()513 nsDocumentViewer::nsDocumentViewer()
514     : mParentWidget(nullptr),
515       mAttachedToParent(false),
516       mNumURLStarts(0),
517       mDestroyBlockedCount(0),
518       mStopped(false),
519       mLoaded(false),
520       mDeferredWindowClose(false),
521       mIsSticky(true),
522       mInPermitUnload(false),
523       mInPermitUnloadPrompt(false),
524 #ifdef NS_PRINTING
525       mClosingWhilePrinting(false),
526 #endif  // NS_PRINTING
527       mReloadEncodingSource(kCharsetUninitialized),
528       mReloadEncoding(nullptr),
529       mIsPageMode(false),
530       mInitializedForPrintPreview(false),
531       mHidden(false) {
532   PrepareToStartLoad();
533 }
534 
535 NS_IMPL_ADDREF(nsDocumentViewer)
NS_IMPL_RELEASE(nsDocumentViewer)536 NS_IMPL_RELEASE(nsDocumentViewer)
537 
538 NS_INTERFACE_MAP_BEGIN(nsDocumentViewer)
539   NS_INTERFACE_MAP_ENTRY(nsIContentViewer)
540   NS_INTERFACE_MAP_ENTRY(nsIContentViewerEdit)
541   NS_INTERFACE_MAP_ENTRY(nsIDocumentViewerPrint)
542   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentViewer)
543 #ifdef NS_PRINTING
544   NS_INTERFACE_MAP_ENTRY(nsIWebBrowserPrint)
545 #endif
546 NS_INTERFACE_MAP_END
547 
548 nsDocumentViewer::~nsDocumentViewer() {
549   if (mDocument) {
550     Close(nullptr);
551     mDocument->Destroy();
552   }
553 
554 #ifdef NS_PRINTING
555   if (mPrintJob) {
556     mPrintJob->Destroy();
557     mPrintJob = nullptr;
558   }
559 #endif
560 
561   MOZ_RELEASE_ASSERT(mDestroyBlockedCount == 0);
562   NS_ASSERTION(!mPresShell && !mPresContext,
563                "User did not call nsIContentViewer::Destroy");
564   if (mPresShell || mPresContext) {
565     // Make sure we don't hand out a reference to the content viewer to
566     // the SHEntry!
567     mSHEntry = nullptr;
568 
569     Destroy();
570   }
571 
572   if (mSelectionListener) {
573     mSelectionListener->Disconnect();
574   }
575 
576   RemoveFocusListener();
577 
578   // XXX(?) Revoke pending invalidate events
579 }
580 
581 /*
582  * This method is called by the Document Loader once a document has
583  * been created for a particular data stream...  The content viewer
584  * must cache this document for later use when Init(...) is called.
585  *
586  * This method is also called when an out of band document.write() happens.
587  * In that case, the document passed in is the same as the previous document.
588  */
589 /* virtual */
LoadStart(Document * aDocument)590 void nsDocumentViewer::LoadStart(Document* aDocument) {
591   MOZ_ASSERT(aDocument);
592 
593   if (!mDocument) {
594     mDocument = aDocument;
595   }
596 }
597 
RemoveFocusListener()598 void nsDocumentViewer::RemoveFocusListener() {
599   if (RefPtr<nsDocViewerFocusListener> oldListener =
600           std::move(mFocusListener)) {
601     oldListener->Disconnect();
602     if (mDocument) {
603       mDocument->RemoveEventListener(u"focus"_ns, oldListener, false);
604       mDocument->RemoveEventListener(u"blur"_ns, oldListener, false);
605     }
606   }
607 }
608 
ReinitializeFocusListener()609 void nsDocumentViewer::ReinitializeFocusListener() {
610   RemoveFocusListener();
611   mFocusListener = new nsDocViewerFocusListener(this);
612   if (mDocument) {
613     mDocument->AddEventListener(u"focus"_ns, mFocusListener, false, false);
614     mDocument->AddEventListener(u"blur"_ns, mFocusListener, false, false);
615   }
616 }
617 
SyncParentSubDocMap()618 nsresult nsDocumentViewer::SyncParentSubDocMap() {
619   nsCOMPtr<nsIDocShell> docShell(mContainer);
620   if (!docShell) {
621     return NS_OK;
622   }
623 
624   nsCOMPtr<nsPIDOMWindowOuter> pwin(docShell->GetWindow());
625   if (!mDocument || !pwin) {
626     return NS_OK;
627   }
628 
629   nsCOMPtr<Element> element = pwin->GetFrameElementInternal();
630   if (!element) {
631     return NS_OK;
632   }
633 
634   nsCOMPtr<nsIDocShellTreeItem> parent;
635   docShell->GetInProcessParent(getter_AddRefs(parent));
636 
637   nsCOMPtr<nsPIDOMWindowOuter> parent_win =
638       parent ? parent->GetWindow() : nullptr;
639   if (!parent_win) {
640     return NS_OK;
641   }
642 
643   nsCOMPtr<Document> parent_doc = parent_win->GetDoc();
644   if (!parent_doc) {
645     return NS_OK;
646   }
647 
648   if (mDocument && parent_doc->GetSubDocumentFor(element) != mDocument &&
649       parent_doc->EventHandlingSuppressed()) {
650     mDocument->SuppressEventHandling(parent_doc->EventHandlingSuppressed());
651   }
652   return parent_doc->SetSubDocumentFor(element, mDocument);
653 }
654 
655 NS_IMETHODIMP
SetContainer(nsIDocShell * aContainer)656 nsDocumentViewer::SetContainer(nsIDocShell* aContainer) {
657   mContainer = static_cast<nsDocShell*>(aContainer);
658 
659   // We're loading a new document into the window where this document
660   // viewer lives, sync the parent document's frame element -> sub
661   // document map
662 
663   return SyncParentSubDocMap();
664 }
665 
666 NS_IMETHODIMP
GetContainer(nsIDocShell ** aResult)667 nsDocumentViewer::GetContainer(nsIDocShell** aResult) {
668   NS_ENSURE_ARG_POINTER(aResult);
669 
670   nsCOMPtr<nsIDocShell> container(mContainer);
671   container.swap(*aResult);
672   return NS_OK;
673 }
674 
675 NS_IMETHODIMP
Init(nsIWidget * aParentWidget,const nsIntRect & aBounds,WindowGlobalChild * aActor)676 nsDocumentViewer::Init(nsIWidget* aParentWidget, const nsIntRect& aBounds,
677                        WindowGlobalChild* aActor) {
678   return InitInternal(aParentWidget, nullptr, aActor, aBounds, true);
679 }
680 
InitPresentationStuff(bool aDoInitialReflow)681 nsresult nsDocumentViewer::InitPresentationStuff(bool aDoInitialReflow) {
682   // We assert this because initializing the pres shell could otherwise cause
683   // re-entrancy into nsDocumentViewer methods, which might cause a different
684   // pres shell to be created.  Callers of InitPresentationStuff should ensure
685   // the call is appropriately bounded by an nsAutoScriptBlocker to decide
686   // when it is safe for these re-entrant calls to be made.
687   MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(),
688              "InitPresentationStuff must only be called when scripts are "
689              "blocked");
690 
691 #ifdef NS_PRINTING
692   // When getting printed, either for print or print preview, the print job
693   // takes care of setting up the presentation of the document.
694   if (mPrintJob) {
695     return NS_OK;
696   }
697 #endif
698 
699   NS_ASSERTION(!mPresShell, "Someone should have destroyed the presshell!");
700 
701   // Now make the shell for the document
702   mPresShell = mDocument->CreatePresShell(mPresContext, mViewManager);
703   if (!mPresShell) {
704     return NS_ERROR_FAILURE;
705   }
706 
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)
713     //
714     // Note that we are flushing before we add mPresShell as an observer
715     // to avoid bogus notifications.
716     mDocument->FlushPendingNotifications(FlushType::ContentAndNotify);
717   }
718 
719   mPresShell->BeginObservingDocument();
720 
721   // Initialize our view manager
722 
723   {
724     int32_t p2a = mPresContext->AppUnitsPerDevPixel();
725     MOZ_ASSERT(
726         p2a ==
727         mPresContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom());
728 
729     nscoord width = p2a * mBounds.width;
730     nscoord height = p2a * mBounds.height;
731 
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();
736   }
737 
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);
746     }
747 
748     mPresContext->SetSafeAreaInsets(windowSafeAreaInsets);
749   }
750 
751   if (aDoInitialReflow) {
752     RefPtr<PresShell> presShell = mPresShell;
753     // Initial reflow
754     presShell->Initialize();
755   }
756 
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);
761   }
762 
763   RefPtr<mozilla::dom::Selection> selection = GetDocumentSelection();
764   if (!selection) {
765     return NS_ERROR_FAILURE;
766   }
767 
768   selection->AddSelectionListener(mSelectionListener);
769 
770   ReinitializeFocusListener();
771 
772   if (aDoInitialReflow && mDocument) {
773     nsCOMPtr<Document> document = mDocument;
774     document->ScrollToRef();
775   }
776 
777   return NS_OK;
778 }
779 
CreatePresContext(Document * aDocument,nsPresContext::nsPresContextType aType,nsView * aContainerView)780 static nsPresContext* CreatePresContext(Document* aDocument,
781                                         nsPresContext::nsPresContextType aType,
782                                         nsView* aContainerView) {
783   if (aContainerView) {
784     return new nsPresContext(aDocument, aType);
785   }
786   return new nsRootPresContext(aDocument, aType);
787 }
788 
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
InitInternal(nsIWidget * aParentWidget,nsISupports * aState,WindowGlobalChild * aActor,const nsIntRect & aBounds,bool aDoCreation,bool aNeedMakeCX,bool aForceSetNewDocument)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;
801 
802   mParentWidget = aParentWidget;  // not ref counted
803   mBounds = aBounds;
804 
805   nsresult rv = NS_OK;
806   NS_ENSURE_TRUE(mDocument, NS_ERROR_NULL_POINTER);
807 
808   nsView* containerView = FindContainerView();
809 
810   bool makeCX = false;
811   if (aDoCreation) {
812     nsresult rv = CreateDeviceContext(containerView);
813     NS_ENSURE_SUCCESS(rv, rv);
814 
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);
830       }
831       NS_ENSURE_TRUE(mPresContext, NS_ERROR_OUT_OF_MEMORY);
832 
833       nsresult rv = mPresContext->Init(mDeviceContext);
834       if (NS_FAILED(rv)) {
835         mPresContext = nullptr;
836         return rv;
837       }
838 
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
846     }
847 
848     if (mPresContext) {
849       // Create the ViewManager and Root View...
850 
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...
855 
856       rv = MakeWindow(nsSize(mPresContext->DevPixelsToAppUnits(aBounds.width),
857                              mPresContext->DevPixelsToAppUnits(aBounds.height)),
858                       containerView);
859       NS_ENSURE_SUCCESS(rv, rv);
860       Hide();
861 
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);
876       }
877 #endif
878     } else {
879       // Avoid leaking the old viewer.
880       if (mPreviousViewer) {
881         mPreviousViewer->Destroy();
882         mPreviousViewer = nullptr;
883       }
884     }
885   }
886 
887   nsCOMPtr<nsIInterfaceRequestor> requestor(mContainer);
888   if (requestor) {
889     // Set script-context-owner in the document
890 
891     nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(requestor);
892 
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;
900         }
901       }
902     }
903   }
904 
905   if (aDoCreation && mPresContext) {
906     // The ViewManager and Root View was created above (in
907     // MakeWindow())...
908 
909     rv = InitPresentationStuff(!makeCX);
910   }
911 
912   return rv;
913 }
914 
SetNavigationTiming(nsDOMNavigationTiming * timing)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);
919   }
920 }
921 
922 //
923 // LoadComplete(aStatus)
924 //
925 //   aStatus - The status returned from loading the document.
926 //
927 // This method is called by the container when the document has been
928 // completely loaded.
929 //
930 NS_IMETHODIMP
LoadComplete(nsresult aStatus)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.
936   */
937   RefPtr<nsDocumentViewer> kungFuDeathGrip(this);
938 
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);
946   }
947 
948   nsresult rv = NS_OK;
949   NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
950 
951   // First, get the window from the document...
952   nsCOMPtr<nsPIDOMWindowOuter> window = mDocument->GetWindow();
953 
954   mLoaded = true;
955 
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;
973 
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.
977 
978     RefPtr<nsDocShell> docShell = nsDocShell::Cast(window->GetDocShell());
979     NS_ENSURE_TRUE(docShell, NS_ERROR_UNEXPECTED);
980 
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);
1006 
1007       RefPtr<nsDOMNavigationTiming> timing(d->GetNavigationTiming());
1008       if (timing) {
1009         timing->NotifyLoadEventStart();
1010       }
1011 
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);
1022       }
1023 
1024       // Notify any devtools about the load.
1025       RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
1026 
1027       if (timelines && timelines->HasConsumer(docShell)) {
1028         timelines->AddMarkerForDocShell(
1029             docShell, MakeUnique<DocLoadingTimelineMarker>("document::Load"));
1030       }
1031 
1032       nsPIDOMWindowInner* innerWindow = window->GetCurrentInnerWindow();
1033       RefPtr<DocGroup> docGroup = d->GetDocGroup();
1034       // It is possible that the parent document's load event fires earlier than
1035       // childs' load event, and in this case we need to fire some artificial
1036       // load events to make the parent thinks the load events for child has
1037       // been done
1038       if (innerWindow && DocGroup::TryToLoadIframesInBackground()) {
1039         nsTArray<nsCOMPtr<nsIDocShell>> docShells;
1040         nsCOMPtr<nsIDocShell> container(mContainer);
1041         if (container) {
1042           int32_t count;
1043           container->GetInProcessChildCount(&count);
1044           // We first find all background loading iframes that need to
1045           // fire artificial load events, and instead of firing them as
1046           // soon as we find them, we store them in an array, to prevent
1047           // us from skipping some events.
1048           for (int32_t i = 0; i < count; ++i) {
1049             nsCOMPtr<nsIDocShellTreeItem> child;
1050             container->GetInProcessChildAt(i, getter_AddRefs(child));
1051             nsCOMPtr<nsIDocShell> childIDocShell = do_QueryInterface(child);
1052             RefPtr<nsDocShell> docShell = nsDocShell::Cast(childIDocShell);
1053             if (docShell && docShell->TreatAsBackgroundLoad() &&
1054                 docShell->GetDocument()->GetReadyStateEnum() <
1055                     Document::READYSTATE_COMPLETE) {
1056               docShells.AppendElement(childIDocShell);
1057             }
1058           }
1059 
1060           // Re-iterate the stored docShells to fire artificial load events
1061           for (size_t i = 0; i < docShells.Length(); ++i) {
1062             RefPtr<nsDocShell> docShell = nsDocShell::Cast(docShells[i]);
1063             if (docShell && docShell->TreatAsBackgroundLoad() &&
1064                 docShell->GetDocument()->GetReadyStateEnum() <
1065                     Document::READYSTATE_COMPLETE) {
1066               nsEventStatus status = nsEventStatus_eIgnore;
1067               WidgetEvent event(true, eLoad);
1068               event.mFlags.mBubbles = false;
1069               event.mFlags.mCancelable = false;
1070 
1071               nsCOMPtr<nsPIDOMWindowOuter> win = docShell->GetWindow();
1072               nsCOMPtr<Element> element = win->GetFrameElementInternal();
1073 
1074               docShell->SetFakeOnLoadDispatched();
1075               EventDispatcher::Dispatch(element, nullptr, &event, nullptr,
1076                                         &status);
1077             }
1078           }
1079         }
1080       }
1081 
1082       d->SetLoadEventFiring(true);
1083       RefPtr<nsPresContext> presContext = mPresContext;
1084       EventDispatcher::Dispatch(window, presContext, &event, nullptr, &status);
1085       d->SetLoadEventFiring(false);
1086 
1087       if (docGroup && docShell->TreatAsBackgroundLoad()) {
1088         docGroup->TryFlushIframePostMessages(docShell->GetOuterWindowID());
1089       }
1090 
1091       if (timing) {
1092         timing->NotifyLoadEventEnd();
1093       }
1094 
1095       if (innerWindow) {
1096         innerWindow->QueuePerformanceNavigationTiming();
1097       }
1098     }
1099   } else {
1100     // XXX: Should fire error event to the document...
1101 
1102     // If our load was explicitly aborted, then we want to set our
1103     // readyState to COMPLETE, and fire a readystatechange event.
1104     if (aStatus == NS_BINDING_ABORTED && mDocument) {
1105       mDocument->NotifyAbortedLoad();
1106     }
1107   }
1108 
1109   // Notify the document that it has been shown (regardless of whether
1110   // it was just loaded). Note: mDocument may be null now if the above
1111   // firing of onload caused the document to unload. Or, mDocument may not be
1112   // the "current active" document, if the above firing of onload caused our
1113   // docshell to navigate away. NOTE: In this latter scenario, it's likely that
1114   // we fired pagehide (when navigating away) without ever having fired
1115   // pageshow, and that's pretty broken... Fortunately, this should be rare.
1116   // (It requires us to spin the event loop in onload handler, e.g. via sync
1117   // XHR, in order for the navigation-away to happen before onload completes.)
1118   // We skip firing pageshow if we're currently handling unload, or if loading
1119   // was explicitly aborted.
1120   if (mDocument && mDocument->IsCurrentActiveDocument() &&
1121       aStatus != NS_BINDING_ABORTED) {
1122     // Re-get window, since it might have changed during above firing of onload
1123     window = mDocument->GetWindow();
1124     if (window) {
1125       nsIDocShell* docShell = window->GetDocShell();
1126       bool isInUnload;
1127       if (docShell && NS_SUCCEEDED(docShell->GetIsInUnload(&isInUnload)) &&
1128           !isInUnload) {
1129         mDocument->OnPageShow(restoring, nullptr);
1130       }
1131     }
1132   }
1133 
1134   if (!mStopped) {
1135     if (mDocument) {
1136       nsCOMPtr<Document> document = mDocument;
1137       document->ScrollToRef();
1138     }
1139 
1140     // Now that the document has loaded, we can tell the presshell
1141     // to unsuppress painting.
1142     if (mPresShell) {
1143       RefPtr<PresShell> presShell = mPresShell;
1144       presShell->UnsuppressPainting();
1145       // mPresShell could have been removed now, see bug 378682/421432
1146       if (mPresShell) {
1147         mPresShell->LoadComplete();
1148       }
1149     }
1150   }
1151 
1152   if (mDocument && !restoring) {
1153     mDocument->LoadEventFired();
1154   }
1155 
1156   // It's probably a good idea to GC soon since we have finished loading.
1157   nsJSContext::PokeGC(
1158       JS::GCReason::LOAD_END,
1159       mDocument ? mDocument->GetWrapperPreserveColor() : nullptr);
1160 
1161 #ifdef NS_PRINTING
1162   // Check to see if someone tried to print during the load
1163   if (window) {
1164     auto* outerWin = nsGlobalWindowOuter::Cast(window);
1165     outerWin->StopDelayingPrintingUntilAfterLoad();
1166     if (outerWin->DelayedPrintUntilAfterLoad()) {
1167       // We call into the inner because it ensures there's an active document
1168       // and such, and it also waits until the whole thing completes, which is
1169       // nice because it allows us to close if needed right here.
1170       if (RefPtr<nsPIDOMWindowInner> inner = window->GetCurrentInnerWindow()) {
1171         nsGlobalWindowInner::Cast(inner)->Print(IgnoreErrors());
1172       }
1173       if (outerWin->DelayedCloseForPrinting()) {
1174         outerWin->Close();
1175       }
1176     } else {
1177       MOZ_ASSERT(!outerWin->DelayedCloseForPrinting());
1178     }
1179   }
1180 #endif
1181 
1182   return rv;
1183 }
1184 
GetLoadCompleted()1185 bool nsDocumentViewer::GetLoadCompleted() { return mLoaded; }
1186 
GetIsStopped()1187 bool nsDocumentViewer::GetIsStopped() { return mStopped; }
1188 
1189 NS_IMETHODIMP
PermitUnload(PermitUnloadAction aAction,bool * aPermitUnload)1190 nsDocumentViewer::PermitUnload(PermitUnloadAction aAction,
1191                                bool* aPermitUnload) {
1192   // We're going to be running JS and nested event loops, which could cause our
1193   // DocShell to be destroyed. Make sure we stay alive until the end of the
1194   // function.
1195   RefPtr<nsDocumentViewer> kungFuDeathGrip(this);
1196 
1197   if (StaticPrefs::dom_disable_beforeunload()) {
1198     aAction = eDontPromptAndUnload;
1199   }
1200 
1201   *aPermitUnload = true;
1202 
1203   RefPtr<BrowsingContext> bc = mContainer->GetBrowsingContext();
1204   if (!bc) {
1205     return NS_OK;
1206   }
1207 
1208   // Per spec, we need to increase the ignore-opens-during-unload counter while
1209   // dispatching the "beforeunload" event on both the document we're currently
1210   // dispatching the event to and the document that we explicitly asked to
1211   // unload.
1212   IgnoreOpensDuringUnload ignoreOpens(mDocument);
1213 
1214   bool foundBlocker = false;
1215   bool foundOOPListener = false;
1216   bc->PreOrderWalk([&](BrowsingContext* aBC) {
1217     if (!aBC->IsInProcess()) {
1218       WindowContext* wc = aBC->GetCurrentWindowContext();
1219       if (wc && wc->HasBeforeUnload()) {
1220         foundOOPListener = true;
1221       }
1222     } else if (aBC->GetDocShell()) {
1223       nsCOMPtr<nsIContentViewer> contentViewer(
1224           aBC->GetDocShell()->GetContentViewer());
1225       if (contentViewer &&
1226           contentViewer->DispatchBeforeUnload() == eRequestBlockNavigation) {
1227         foundBlocker = true;
1228       }
1229     }
1230   });
1231 
1232   if (!foundOOPListener) {
1233     if (!foundBlocker) {
1234       return NS_OK;
1235     }
1236     if (aAction != ePrompt) {
1237       *aPermitUnload = aAction == eDontPromptAndUnload;
1238       return NS_OK;
1239     }
1240   }
1241 
1242   // NB: we nullcheck mDocument because it might now be dead as a result of
1243   // the event being dispatched.
1244   RefPtr<WindowGlobalChild> wgc(mDocument ? mDocument->GetWindowGlobalChild()
1245                                           : nullptr);
1246   if (!wgc) {
1247     return NS_OK;
1248   }
1249 
1250   nsAutoSyncOperation sync(mDocument, SyncOperationBehavior::eSuspendInput);
1251   AutoSuppressEventHandlingAndSuspend seh(bc->Group());
1252 
1253   mInPermitUnloadPrompt = true;
1254 
1255   bool done = false;
1256   wgc->SendCheckPermitUnload(
1257       foundBlocker, aAction,
1258       [&](bool aPermit) {
1259         done = true;
1260         *aPermitUnload = aPermit;
1261       },
1262       [&](auto) {
1263         // If the prompt aborted, we tell our consumer that it is not allowed
1264         // to unload the page. One reason that prompts abort is that the user
1265         // performed some action that caused the page to unload while our prompt
1266         // was active. In those cases we don't want our consumer to also unload
1267         // the page.
1268         //
1269         // XXX: Are there other cases where prompts can abort? Is it ok to
1270         //      prevent unloading the page in those cases?
1271         done = true;
1272         *aPermitUnload = false;
1273       });
1274 
1275   SpinEventLoopUntil("nsDocumentViewer::PermitUnload"_ns,
1276                      [&]() { return done; });
1277 
1278   mInPermitUnloadPrompt = false;
1279   return NS_OK;
1280 }
1281 
1282 MOZ_CAN_RUN_SCRIPT_BOUNDARY PermitUnloadResult
DispatchBeforeUnload()1283 nsDocumentViewer::DispatchBeforeUnload() {
1284   AutoDontWarnAboutSyncXHR disableSyncXHRWarning;
1285 
1286   if (!mDocument || mInPermitUnload || mInPermitUnloadPrompt) {
1287     return eAllowNavigation;
1288   }
1289 
1290   // First, get the script global object from the document...
1291   RefPtr<nsGlobalWindowOuter> window =
1292       nsGlobalWindowOuter::Cast(mDocument->GetWindow());
1293   if (!window) {
1294     // This is odd, but not fatal
1295     NS_WARNING("window not set for document!");
1296     return eAllowNavigation;
1297   }
1298 
1299   NS_ASSERTION(nsContentUtils::IsSafeToRunScript(), "This is unsafe");
1300 
1301   // https://html.spec.whatwg.org/multipage/browsing-the-web.html#prompt-to-unload-a-document
1302   // Create an RAII object on mDocument that will increment the
1303   // should-ignore-opens-during-unload counter on initialization
1304   // and decrement it again when it goes out of score (regardless
1305   // of how we exit this function).
1306   IgnoreOpensDuringUnload ignoreOpens(mDocument);
1307 
1308   // Now, fire an BeforeUnload event to the document and see if it's ok
1309   // to unload...
1310   nsPresContext* presContext = mDocument->GetPresContext();
1311   RefPtr<BeforeUnloadEvent> event =
1312       new BeforeUnloadEvent(mDocument, presContext, nullptr);
1313   event->InitEvent(u"beforeunload"_ns, false, true);
1314 
1315   // Dispatching to |window|, but using |document| as the target.
1316   event->SetTarget(mDocument);
1317   event->SetTrusted(true);
1318 
1319   // In evil cases we might be destroyed while handling the
1320   // onbeforeunload event, don't let that happen. (see also bug#331040)
1321   RefPtr<nsDocumentViewer> kungFuDeathGrip(this);
1322 
1323   {
1324     // Never permit popups from the beforeunload handler, no matter
1325     // how we get here.
1326     AutoPopupStatePusher popupStatePusher(PopupBlocker::openAbused, true);
1327 
1328     RefPtr<BrowsingContext> bc = mContainer->GetBrowsingContext();
1329     NS_ASSERTION(bc, "should have a browsing context in document viewer");
1330 
1331     // Never permit dialogs from the beforeunload handler
1332     nsGlobalWindowOuter::TemporarilyDisableDialogs disableDialogs(bc);
1333 
1334     Document::PageUnloadingEventTimeStamp timestamp(mDocument);
1335 
1336     mInPermitUnload = true;
1337     RefPtr<nsPresContext> presContext = mPresContext;
1338     // TODO: Bug 1506441
1339     EventDispatcher::DispatchDOMEvent(MOZ_KnownLive(ToSupports(window)),
1340                                       nullptr, event, presContext, nullptr);
1341     mInPermitUnload = false;
1342   }
1343 
1344   nsAutoString text;
1345   event->GetReturnValue(text);
1346 
1347   // NB: we nullcheck mDocument because it might now be dead as a result of
1348   // the event being dispatched.
1349   if (window->AreDialogsEnabled() && mDocument &&
1350       !(mDocument->GetSandboxFlags() & SANDBOXED_MODALS) &&
1351       (!StaticPrefs::dom_require_user_interaction_for_beforeunload() ||
1352        mDocument->UserHasInteracted()) &&
1353       (event->WidgetEventPtr()->DefaultPrevented() || !text.IsEmpty())) {
1354     return eRequestBlockNavigation;
1355   }
1356   return eAllowNavigation;
1357 }
1358 
1359 NS_IMETHODIMP
GetBeforeUnloadFiring(bool * aInEvent)1360 nsDocumentViewer::GetBeforeUnloadFiring(bool* aInEvent) {
1361   *aInEvent = mInPermitUnload;
1362   return NS_OK;
1363 }
1364 
1365 NS_IMETHODIMP
GetInPermitUnload(bool * aInEvent)1366 nsDocumentViewer::GetInPermitUnload(bool* aInEvent) {
1367   *aInEvent = mInPermitUnloadPrompt;
1368   return NS_OK;
1369 }
1370 
1371 NS_IMETHODIMP
PageHide(bool aIsUnload)1372 nsDocumentViewer::PageHide(bool aIsUnload) {
1373   AutoDontWarnAboutSyncXHR disableSyncXHRWarning;
1374 
1375   mHidden = true;
1376 
1377   if (!mDocument) {
1378     return NS_ERROR_NULL_POINTER;
1379   }
1380 
1381   if (aIsUnload) {
1382     // Poke the GC. The window might be collectable garbage now.
1383     nsJSContext::PokeGC(JS::GCReason::PAGE_HIDE,
1384                         mDocument->GetWrapperPreserveColor(),
1385                         TimeDuration::FromMilliseconds(
1386                             StaticPrefs::javascript_options_gc_delay() * 2));
1387   }
1388 
1389   mDocument->OnPageHide(!aIsUnload, nullptr);
1390 
1391   // inform the window so that the focus state is reset.
1392   NS_ENSURE_STATE(mDocument);
1393   nsPIDOMWindowOuter* window = mDocument->GetWindow();
1394   if (window) window->PageHidden();
1395 
1396   if (aIsUnload) {
1397     // if Destroy() was called during OnPageHide(), mDocument is nullptr.
1398     NS_ENSURE_STATE(mDocument);
1399 
1400     // First, get the window from the document...
1401     RefPtr<nsPIDOMWindowOuter> window = mDocument->GetWindow();
1402 
1403     if (!window) {
1404       // Fail if no window is available...
1405       NS_WARNING("window not set for document!");
1406       return NS_ERROR_NULL_POINTER;
1407     }
1408 
1409     // https://html.spec.whatwg.org/multipage/browsing-the-web.html#unload-a-document
1410     // Create an RAII object on mDocument that will increment the
1411     // should-ignore-opens-during-unload counter on initialization
1412     // and decrement it again when it goes out of scope.
1413     IgnoreOpensDuringUnload ignoreOpens(mDocument);
1414 
1415     // Now, fire an Unload event to the document...
1416     nsEventStatus status = nsEventStatus_eIgnore;
1417     WidgetEvent event(true, eUnload);
1418     event.mFlags.mBubbles = false;
1419     // XXX Dispatching to |window|, but using |document| as the target.
1420     event.mTarget = mDocument;
1421 
1422     // Never permit popups from the unload handler, no matter how we get
1423     // here.
1424     AutoPopupStatePusher popupStatePusher(PopupBlocker::openAbused, true);
1425 
1426     Document::PageUnloadingEventTimeStamp timestamp(mDocument);
1427 
1428     RefPtr<nsPresContext> presContext = mPresContext;
1429     EventDispatcher::Dispatch(window, presContext, &event, nullptr, &status);
1430   }
1431 
1432   // look for open menupopups and close them after the unload event, in case
1433   // the unload event listeners open any new popups
1434   nsContentUtils::HidePopupsInDocument(mDocument);
1435 
1436   return NS_OK;
1437 }
1438 
AttachContainerRecurse(nsIDocShell * aShell)1439 static void AttachContainerRecurse(nsIDocShell* aShell) {
1440   nsCOMPtr<nsIContentViewer> viewer;
1441   aShell->GetContentViewer(getter_AddRefs(viewer));
1442   if (viewer) {
1443     viewer->SetIsHidden(false);
1444     Document* doc = viewer->GetDocument();
1445     if (doc) {
1446       doc->SetContainer(static_cast<nsDocShell*>(aShell));
1447     }
1448     if (PresShell* presShell = viewer->GetPresShell()) {
1449       presShell->SetForwardingContainer(WeakPtr<nsDocShell>());
1450     }
1451   }
1452 
1453   // Now recurse through the children
1454   int32_t childCount;
1455   aShell->GetInProcessChildCount(&childCount);
1456   for (int32_t i = 0; i < childCount; ++i) {
1457     nsCOMPtr<nsIDocShellTreeItem> childItem;
1458     aShell->GetInProcessChildAt(i, getter_AddRefs(childItem));
1459     nsCOMPtr<nsIDocShell> shell = do_QueryInterface(childItem);
1460     AttachContainerRecurse(shell);
1461   }
1462 }
1463 
1464 NS_IMETHODIMP
Open(nsISupports * aState,nsISHEntry * aSHEntry)1465 nsDocumentViewer::Open(nsISupports* aState, nsISHEntry* aSHEntry) {
1466   NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_INITIALIZED);
1467 
1468   if (mDocument) {
1469     mDocument->SetContainer(mContainer);
1470   }
1471 
1472   nsresult rv = InitInternal(mParentWidget, aState, nullptr, mBounds, false);
1473   NS_ENSURE_SUCCESS(rv, rv);
1474 
1475   mHidden = false;
1476 
1477   if (mPresShell) mPresShell->SetForwardingContainer(WeakPtr<nsDocShell>());
1478 
1479   // Rehook the child presentations.  The child shells are still in
1480   // session history, so get them from there.
1481 
1482   if (aSHEntry) {
1483     nsCOMPtr<nsIDocShellTreeItem> item;
1484     int32_t itemIndex = 0;
1485     while (NS_SUCCEEDED(
1486                aSHEntry->ChildShellAt(itemIndex++, getter_AddRefs(item))) &&
1487            item) {
1488       nsCOMPtr<nsIDocShell> shell = do_QueryInterface(item);
1489       AttachContainerRecurse(shell);
1490     }
1491   }
1492 
1493   SyncParentSubDocMap();
1494 
1495   ReinitializeFocusListener();
1496 
1497   // XXX re-enable image animations once that works correctly
1498 
1499   PrepareToStartLoad();
1500 
1501   // When loading a page from the bfcache with puppet widgets, we do the
1502   // widget attachment here (it is otherwise done in MakeWindow, which is
1503   // called for non-bfcache pages in the history, but not bfcache pages).
1504   // Attachment is necessary, since we get detached when another page
1505   // is browsed to. That is, if we are one page A, then when we go to
1506   // page B, we detach. So page A's view has no widget. If we then go
1507   // back to it, and it is in the bfcache, we will use that view, which
1508   // doesn't have a widget. The attach call here will properly attach us.
1509   if (nsIWidget::UsePuppetWidgets() && mPresContext &&
1510       ShouldAttachToTopLevel()) {
1511     // If the old view is already attached to our parent, detach
1512     DetachFromTopLevelWidget();
1513 
1514     nsViewManager* vm = GetViewManager();
1515     MOZ_ASSERT(vm, "no view manager");
1516     nsView* v = vm->GetRootView();
1517     MOZ_ASSERT(v, "no root view");
1518     MOZ_ASSERT(mParentWidget, "no mParentWidget to set");
1519     v->AttachToTopLevelWidget(mParentWidget);
1520 
1521     mAttachedToParent = true;
1522   }
1523 
1524   return NS_OK;
1525 }
1526 
1527 NS_IMETHODIMP
Close(nsISHEntry * aSHEntry)1528 nsDocumentViewer::Close(nsISHEntry* aSHEntry) {
1529   // All callers are supposed to call close to break circular
1530   // references.  If we do this stuff in the destructor, the
1531   // destructor might never be called (especially if we're being
1532   // used from JS.
1533 
1534   mSHEntry = aSHEntry;
1535 
1536   // Close is also needed to disable scripts during paint suppression,
1537   // since we transfer the existing global object to the new document
1538   // that is loaded.  In the future, the global object may become a proxy
1539   // for an object that can be switched in and out so that we don't need
1540   // to disable scripts during paint suppression.
1541 
1542   if (!mDocument) return NS_OK;
1543 
1544   if (mSHEntry) {
1545     if (mBFCachePreventionObserver) {
1546       mBFCachePreventionObserver->Disconnect();
1547     }
1548     mBFCachePreventionObserver = new BFCachePreventionObserver(mDocument);
1549     mDocument->AddMutationObserver(mBFCachePreventionObserver);
1550   }
1551 
1552 #ifdef NS_PRINTING
1553   // A Close was called while we were printing
1554   // so don't clear the ScriptGlobalObject
1555   // or clear the mDocument below
1556   if (mPrintJob && !mClosingWhilePrinting) {
1557     mClosingWhilePrinting = true;
1558   } else
1559 #endif
1560   {
1561     // out of band cleanup of docshell
1562     mDocument->SetScriptGlobalObject(nullptr);
1563 
1564     if (!mSHEntry && mDocument) mDocument->RemovedFromDocShell();
1565   }
1566 
1567   RemoveFocusListener();
1568   return NS_OK;
1569 }
1570 
DetachContainerRecurse(nsIDocShell * aShell)1571 static void DetachContainerRecurse(nsIDocShell* aShell) {
1572   // Unhook this docshell's presentation
1573   aShell->SynchronizeLayoutHistoryState();
1574   nsCOMPtr<nsIContentViewer> viewer;
1575   aShell->GetContentViewer(getter_AddRefs(viewer));
1576   if (viewer) {
1577     if (Document* doc = viewer->GetDocument()) {
1578       doc->SetContainer(nullptr);
1579     }
1580     if (PresShell* presShell = viewer->GetPresShell()) {
1581       auto weakShell = static_cast<nsDocShell*>(aShell);
1582       presShell->SetForwardingContainer(weakShell);
1583     }
1584   }
1585 
1586   // Now recurse through the children
1587   int32_t childCount;
1588   aShell->GetInProcessChildCount(&childCount);
1589   for (int32_t i = 0; i < childCount; ++i) {
1590     nsCOMPtr<nsIDocShellTreeItem> childItem;
1591     aShell->GetInProcessChildAt(i, getter_AddRefs(childItem));
1592     nsCOMPtr<nsIDocShell> shell = do_QueryInterface(childItem);
1593     DetachContainerRecurse(shell);
1594   }
1595 }
1596 
1597 NS_IMETHODIMP
Destroy()1598 nsDocumentViewer::Destroy() {
1599   // Don't let the document get unloaded while we are printing.
1600   // this could happen if we hit the back button during printing.
1601   // We also keep the viewer from being cached in session history, since
1602   // we require all documents there to be sanitized.
1603   if (mDestroyBlockedCount != 0) {
1604     return NS_OK;
1605   }
1606 
1607 #ifdef NS_PRINTING
1608   // Here is where we check to see if the document was still being prepared
1609   // for printing when it was asked to be destroy from someone externally
1610   // This usually happens if the document is unloaded while the user is in the
1611   // Print Dialog
1612   //
1613   // So we flip the bool to remember that the document is going away
1614   // and we can clean up and abort later after returning from the Print Dialog
1615   if (mPrintJob && mPrintJob->CheckBeforeDestroy()) {
1616     return NS_OK;
1617   }
1618 #endif
1619 
1620   // We want to make sure to disconnect mBFCachePreventionObserver before we
1621   // Sanitize() below.
1622   if (mBFCachePreventionObserver) {
1623     mBFCachePreventionObserver->Disconnect();
1624     mBFCachePreventionObserver = nullptr;
1625   }
1626 
1627   if (mSHEntry && mDocument && !mDocument->IsBFCachingAllowed()) {
1628     // Just drop the SHEntry now and pretend like we never even tried to bfcache
1629     // this viewer.  This should only happen when someone calls
1630     // DisallowBFCaching() after CanSavePresentation() already ran.  Ensure that
1631     // the SHEntry has no viewer and its state is synced up.  We want to do this
1632     // via a stack reference, in case those calls mess with our members.
1633     MOZ_LOG(gPageCacheLog, LogLevel::Debug,
1634             ("BFCache not allowed, dropping SHEntry"));
1635     nsCOMPtr<nsISHEntry> shEntry = std::move(mSHEntry);
1636     shEntry->SetContentViewer(nullptr);
1637     shEntry->SyncPresentationState();
1638   }
1639 
1640   // If we were told to put ourselves into session history instead of destroy
1641   // the presentation, do that now.
1642   if (mSHEntry) {
1643     if (mPresShell) mPresShell->Freeze();
1644 
1645     // Make sure the presentation isn't torn down by Hide().
1646     mSHEntry->SetSticky(mIsSticky);
1647     mIsSticky = true;
1648 
1649     // Remove our root view from the view hierarchy.
1650     if (mPresShell) {
1651       nsViewManager* vm = mPresShell->GetViewManager();
1652       if (vm) {
1653         nsView* rootView = vm->GetRootView();
1654 
1655         if (rootView) {
1656           nsView* rootViewParent = rootView->GetParent();
1657           if (rootViewParent) {
1658             nsView* subdocview = rootViewParent->GetParent();
1659             if (subdocview) {
1660               nsIFrame* f = subdocview->GetFrame();
1661               if (f) {
1662                 nsSubDocumentFrame* s = do_QueryFrame(f);
1663                 if (s) {
1664                   s->ClearDisplayItems();
1665                 }
1666               }
1667             }
1668             nsViewManager* parentVM = rootViewParent->GetViewManager();
1669             if (parentVM) {
1670               parentVM->RemoveChild(rootView);
1671             }
1672           }
1673         }
1674       }
1675     }
1676 
1677     Hide();
1678 
1679     // This is after Hide() so that the user doesn't see the inputs clear.
1680     if (mDocument) {
1681       mDocument->Sanitize();
1682     }
1683 
1684     // Reverse ownership. Do this *after* calling sanitize so that sanitize
1685     // doesn't cause mutations that make the SHEntry drop the presentation
1686 
1687     // Grab a reference to mSHEntry before calling into things like
1688     // SyncPresentationState that might mess with our members.
1689     nsCOMPtr<nsISHEntry> shEntry =
1690         std::move(mSHEntry);  // we'll need this below
1691 
1692     MOZ_LOG(gPageCacheLog, LogLevel::Debug,
1693             ("Storing content viewer into cache entry"));
1694     shEntry->SetContentViewer(this);
1695 
1696     // Always sync the presentation state.  That way even if someone screws up
1697     // and shEntry has no window state at this point we'll be ok; we just won't
1698     // cache ourselves.
1699     shEntry->SyncPresentationState();
1700     // XXX Synchronize layout history state to parent once bfcache is supported
1701     //     in session-history-in-parent.
1702 
1703     // Shut down accessibility for the document before we start to tear it down.
1704 #ifdef ACCESSIBILITY
1705     if (mPresShell) {
1706       a11y::DocAccessible* docAcc = mPresShell->GetDocAccessible();
1707       if (docAcc) {
1708         docAcc->Shutdown();
1709       }
1710     }
1711 #endif
1712 
1713     // Break the link from the document/presentation to the docshell, so that
1714     // link traversals cannot affect the currently-loaded document.
1715     // When the presentation is restored, Open() and InitInternal() will reset
1716     // these pointers to their original values.
1717 
1718     if (mDocument) {
1719       mDocument->SetContainer(nullptr);
1720     }
1721     if (mPresShell) {
1722       mPresShell->SetForwardingContainer(mContainer);
1723     }
1724 
1725     // Do the same for our children.  Note that we need to get the child
1726     // docshells from the SHEntry now; the docshell will have cleared them.
1727     nsCOMPtr<nsIDocShellTreeItem> item;
1728     int32_t itemIndex = 0;
1729     while (NS_SUCCEEDED(
1730                shEntry->ChildShellAt(itemIndex++, getter_AddRefs(item))) &&
1731            item) {
1732       nsCOMPtr<nsIDocShell> shell = do_QueryInterface(item);
1733       DetachContainerRecurse(shell);
1734     }
1735 
1736     return NS_OK;
1737   }
1738 
1739   // The document was not put in the bfcache
1740 
1741   // Protect against pres shell destruction running scripts and re-entrantly
1742   // creating a new presentation.
1743   nsAutoScriptBlocker scriptBlocker;
1744 
1745   if (mPresShell) {
1746     DestroyPresShell();
1747   }
1748   if (mDocument) {
1749     mDocument->Destroy();
1750     mDocument = nullptr;
1751   }
1752 
1753   // All callers are supposed to call destroy to break circular
1754   // references.  If we do this stuff in the destructor, the
1755   // destructor might never be called (especially if we're being
1756   // used from JS.
1757 
1758 #ifdef NS_PRINTING
1759   if (mPrintJob) {
1760     RefPtr<nsPrintJob> printJob = std::move(mPrintJob);
1761 #  ifdef NS_PRINT_PREVIEW
1762     if (printJob->CreatedForPrintPreview()) {
1763       printJob->FinishPrintPreview();
1764     }
1765 #  endif
1766     printJob->Destroy();
1767     MOZ_ASSERT(!mPrintJob,
1768                "mPrintJob shouldn't be recreated while destroying it");
1769   }
1770 #endif
1771 
1772   // Avoid leaking the old viewer.
1773   if (mPreviousViewer) {
1774     mPreviousViewer->Destroy();
1775     mPreviousViewer = nullptr;
1776   }
1777 
1778   mDeviceContext = nullptr;
1779 
1780   if (mPresContext) {
1781     DestroyPresContext();
1782   }
1783 
1784   mWindow = nullptr;
1785   mViewManager = nullptr;
1786   mContainer = WeakPtr<nsDocShell>();
1787 
1788   return NS_OK;
1789 }
1790 
1791 NS_IMETHODIMP
Stop(void)1792 nsDocumentViewer::Stop(void) {
1793   NS_ASSERTION(mDocument, "Stop called too early or too late");
1794   if (mDocument) {
1795     mDocument->StopDocumentLoad();
1796   }
1797 
1798   mStopped = true;
1799 
1800   if (!mLoaded && mPresShell) {
1801     // Well, we might as well paint what we have so far.
1802     RefPtr<PresShell> presShell = mPresShell;  // bug 378682
1803     presShell->UnsuppressPainting();
1804   }
1805 
1806   return NS_OK;
1807 }
1808 
1809 NS_IMETHODIMP
GetDOMDocument(Document ** aResult)1810 nsDocumentViewer::GetDOMDocument(Document** aResult) {
1811   NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
1812   nsCOMPtr<Document> document = mDocument;
1813   document.forget(aResult);
1814   return NS_OK;
1815 }
1816 
GetDocument()1817 Document* nsDocumentViewer::GetDocument() { return mDocument; }
1818 
SetDocument(Document * aDocument)1819 nsresult nsDocumentViewer::SetDocument(Document* aDocument) {
1820   // Assumptions:
1821   //
1822   // 1) this document viewer has been initialized with a call to Init().
1823   // 2) the stylesheets associated with the document have been added
1824   // to the document.
1825 
1826   // XXX Right now, this method assumes that the layout of the current
1827   // document hasn't started yet.  More cleanup will probably be
1828   // necessary to make this method work for the case when layout *has*
1829   // occurred for the current document.
1830   // That work can happen when and if it is needed.
1831 
1832   if (!aDocument) return NS_ERROR_NULL_POINTER;
1833 
1834   return SetDocumentInternal(aDocument, false);
1835 }
1836 
1837 NS_IMETHODIMP
SetDocumentInternal(Document * aDocument,bool aForceReuseInnerWindow)1838 nsDocumentViewer::SetDocumentInternal(Document* aDocument,
1839                                       bool aForceReuseInnerWindow) {
1840   MOZ_ASSERT(aDocument);
1841 
1842   // Set new container
1843   aDocument->SetContainer(mContainer);
1844 
1845   if (mDocument != aDocument) {
1846     if (aForceReuseInnerWindow) {
1847       // Transfer the navigation timing information to the new document, since
1848       // we're keeping the same inner and hence should really have the same
1849       // timing information.
1850       aDocument->SetNavigationTiming(mDocument->GetNavigationTiming());
1851     }
1852 
1853     if (mDocument &&
1854         (mDocument->IsStaticDocument() || aDocument->IsStaticDocument())) {
1855       nsContentUtils::AddScriptRunner(NewRunnableMethod(
1856           "Document::Destroy", mDocument, &Document::Destroy));
1857     }
1858 
1859     // Clear the list of old child docshells. Child docshells for the new
1860     // document will be constructed as frames are created.
1861     if (!aDocument->IsStaticDocument()) {
1862       nsCOMPtr<nsIDocShell> node(mContainer);
1863       if (node) {
1864         int32_t count;
1865         node->GetInProcessChildCount(&count);
1866         for (int32_t i = 0; i < count; ++i) {
1867           nsCOMPtr<nsIDocShellTreeItem> child;
1868           node->GetInProcessChildAt(0, getter_AddRefs(child));
1869           node->RemoveChild(child);
1870         }
1871       }
1872     }
1873 
1874     // Replace the old document with the new one. Do this only when
1875     // the new document really is a new document.
1876     mDocument = aDocument;
1877 
1878     // Set the script global object on the new document
1879     nsCOMPtr<nsPIDOMWindowOuter> window =
1880         mContainer ? mContainer->GetWindow() : nullptr;
1881     if (window) {
1882       nsresult rv =
1883           window->SetNewDocument(aDocument, nullptr, aForceReuseInnerWindow);
1884       if (NS_FAILED(rv)) {
1885         Destroy();
1886         return rv;
1887       }
1888     }
1889   }
1890 
1891   nsresult rv = SyncParentSubDocMap();
1892   NS_ENSURE_SUCCESS(rv, rv);
1893 
1894   // Replace the current pres shell with a new shell for the new document
1895 
1896   // Protect against pres shell destruction running scripts and re-entrantly
1897   // creating a new presentation.
1898   nsAutoScriptBlocker scriptBlocker;
1899 
1900   if (mPresShell) {
1901     DestroyPresShell();
1902   }
1903 
1904   if (mPresContext) {
1905     DestroyPresContext();
1906 
1907     mWindow = nullptr;
1908     rv = InitInternal(mParentWidget, nullptr, nullptr, mBounds, true, true,
1909                       false);
1910   }
1911 
1912   return rv;
1913 }
1914 
GetPresShell()1915 PresShell* nsDocumentViewer::GetPresShell() { return mPresShell; }
1916 
GetPresContext()1917 nsPresContext* nsDocumentViewer::GetPresContext() { return mPresContext; }
1918 
GetViewManager()1919 nsViewManager* nsDocumentViewer::GetViewManager() { return mViewManager; }
1920 
1921 NS_IMETHODIMP
GetBounds(nsIntRect & aResult)1922 nsDocumentViewer::GetBounds(nsIntRect& aResult) {
1923   NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
1924   aResult = mBounds;
1925   return NS_OK;
1926 }
1927 
GetPreviousViewer()1928 nsIContentViewer* nsDocumentViewer::GetPreviousViewer() {
1929   return mPreviousViewer;
1930 }
1931 
SetPreviousViewer(nsIContentViewer * aViewer)1932 void nsDocumentViewer::SetPreviousViewer(nsIContentViewer* aViewer) {
1933   // NOTE:  |Show| sets |mPreviousViewer| to null without calling this
1934   // function.
1935 
1936   if (aViewer) {
1937     NS_ASSERTION(!mPreviousViewer,
1938                  "can't set previous viewer when there already is one");
1939 
1940     // In a multiple chaining situation (which occurs when running a thrashing
1941     // test like i-bench or jrgm's tests with no delay), we can build up a
1942     // whole chain of viewers.  In order to avoid this, we always set our
1943     // previous viewer to the MOST previous viewer in the chain, and then dump
1944     // the intermediate link from the chain.  This ensures that at most only 2
1945     // documents are alive and undestroyed at any given time (the one that is
1946     // showing and the one that is loading with painting suppressed). It's very
1947     // important that if this ever gets changed the code before the
1948     // RestorePresentation call in nsDocShell::InternalLoad be changed
1949     // accordingly.
1950     //
1951     // Make sure we hold a strong ref to prevViewer here, since we'll
1952     // tell aViewer to drop it.
1953     nsCOMPtr<nsIContentViewer> prevViewer = aViewer->GetPreviousViewer();
1954     if (prevViewer) {
1955       aViewer->SetPreviousViewer(nullptr);
1956       aViewer->Destroy();
1957       return SetPreviousViewer(prevViewer);
1958     }
1959   }
1960 
1961   mPreviousViewer = aViewer;
1962 }
1963 
1964 NS_IMETHODIMP
SetBoundsWithFlags(const nsIntRect & aBounds,uint32_t aFlags)1965 nsDocumentViewer::SetBoundsWithFlags(const nsIntRect& aBounds,
1966                                      uint32_t aFlags) {
1967   NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
1968 
1969   bool boundsChanged = !mBounds.IsEqualEdges(aBounds);
1970   mBounds = aBounds;
1971 
1972   if (mWindow && !mAttachedToParent) {
1973     // Resize the widget, but don't trigger repaint. Layout will generate
1974     // repaint requests during reflow.
1975     mWindow->Resize(aBounds.x, aBounds.y, aBounds.width, aBounds.height, false);
1976   } else if (mPresContext && mViewManager) {
1977     // Ensure presContext's deviceContext is up to date, as we sometimes get
1978     // here before a resolution-change notification has been fully handled
1979     // during display configuration changes, especially when there are lots
1980     // of windows/widgets competing to handle the notifications.
1981     // (See bug 1154125.)
1982     if (mPresContext->DeviceContext()->CheckDPIChange()) {
1983       mPresContext->UIResolutionChanged();
1984     }
1985 
1986     int32_t p2a = mPresContext->AppUnitsPerDevPixel();
1987     nscoord width = NSIntPixelsToAppUnits(mBounds.width, p2a);
1988     nscoord height = NSIntPixelsToAppUnits(mBounds.height, p2a);
1989     nsView* rootView = mViewManager->GetRootView();
1990     if (boundsChanged && rootView) {
1991       nsRect viewDims = rootView->GetDimensions();
1992       // If the view/frame tree and prescontext visible area already has the new
1993       // size but we did not, then it's likely that we got reflowed in response
1994       // to a call to GetContentSize. Thus there is a disconnect between the
1995       // size on the document viewer/docshell/containing widget and view
1996       // tree/frame tree/prescontext visible area). SetWindowDimensions compares
1997       // to the root view dimenstions to determine if it needs to do anything;
1998       // if they are the same as the new size it won't do anything, but we still
1999       // need to invalidate because what we want to draw to the screen has
2000       // changed.
2001       if (viewDims.width == width && viewDims.height == height) {
2002         nsIFrame* f = rootView->GetFrame();
2003         if (f) {
2004           f->InvalidateFrame();
2005         }
2006       }
2007     }
2008 
2009     mViewManager->SetWindowDimensions(
2010         width, height, !!(aFlags & nsIContentViewer::eDelayResize));
2011   }
2012 
2013   // If there's a previous viewer, it's the one that's actually showing,
2014   // so be sure to resize it as well so it paints over the right area.
2015   // This may slow down the performance of the new page load, but resize
2016   // during load is also probably a relatively unusual condition
2017   // relating to things being hidden while something is loaded.  It so
2018   // happens that Firefox does this a good bit with its infobar, and it
2019   // looks ugly if we don't do this.
2020   if (mPreviousViewer) {
2021     nsCOMPtr<nsIContentViewer> previousViewer = mPreviousViewer;
2022     previousViewer->SetBounds(aBounds);
2023   }
2024 
2025   return NS_OK;
2026 }
2027 
2028 NS_IMETHODIMP
SetBounds(const nsIntRect & aBounds)2029 nsDocumentViewer::SetBounds(const nsIntRect& aBounds) {
2030   return SetBoundsWithFlags(aBounds, 0);
2031 }
2032 
2033 NS_IMETHODIMP
Move(int32_t aX,int32_t aY)2034 nsDocumentViewer::Move(int32_t aX, int32_t aY) {
2035   NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
2036   mBounds.MoveTo(aX, aY);
2037   if (mWindow) {
2038     mWindow->Move(aX, aY);
2039   }
2040   return NS_OK;
2041 }
2042 
2043 NS_IMETHODIMP
Show()2044 nsDocumentViewer::Show() {
2045   NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
2046 
2047   // We don't need the previous viewer anymore since we're not
2048   // displaying it.
2049   if (mPreviousViewer) {
2050     // This little dance *may* only be to keep
2051     // PresShell::EndObservingDocument happy, but I'm not sure.
2052     nsCOMPtr<nsIContentViewer> prevViewer(mPreviousViewer);
2053     mPreviousViewer = nullptr;
2054     prevViewer->Destroy();
2055 
2056     // Make sure we don't have too many cached ContentViewers
2057     nsCOMPtr<nsIDocShellTreeItem> treeItem(mContainer);
2058     if (treeItem) {
2059       // We need to find the root DocShell since only that object has an
2060       // SHistory and we need the SHistory to evict content viewers
2061       nsCOMPtr<nsIDocShellTreeItem> root;
2062       treeItem->GetInProcessSameTypeRootTreeItem(getter_AddRefs(root));
2063       nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(root);
2064       RefPtr<ChildSHistory> history = webNav->GetSessionHistory();
2065       if (!mozilla::SessionHistoryInParent() && history) {
2066         int32_t prevIndex, loadedIndex;
2067         nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(treeItem);
2068         docShell->GetPreviousEntryIndex(&prevIndex);
2069         docShell->GetLoadedEntryIndex(&loadedIndex);
2070         MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
2071                 ("About to evict content viewers: prev=%d, loaded=%d",
2072                  prevIndex, loadedIndex));
2073         history->LegacySHistory()->EvictOutOfRangeContentViewers(loadedIndex);
2074       }
2075     }
2076   }
2077 
2078   if (mWindow) {
2079     // When attached to a top level xul window, we do not need to call
2080     // Show on the widget. Underlying window management code handles
2081     // this when the window is initialized.
2082     if (!mAttachedToParent) {
2083       mWindow->Show(true);
2084     }
2085   }
2086 
2087   // Hold on to the document so we can use it after the script blocker below
2088   // has been released (which might re-entrantly call into other
2089   // nsDocumentViewer methods).
2090   nsCOMPtr<Document> document = mDocument;
2091 
2092   if (mDocument && !mPresShell) {
2093     // The InitPresentationStuff call below requires a script blocker, because
2094     // its PresShell::Initialize call can cause scripts to run and therefore
2095     // re-entrant calls to nsDocumentViewer methods to be made.
2096     nsAutoScriptBlocker scriptBlocker;
2097 
2098     NS_ASSERTION(!mWindow, "Window already created but no presshell?");
2099 
2100     nsCOMPtr<nsIBaseWindow> base_win(mContainer);
2101     if (base_win) {
2102       base_win->GetParentWidget(&mParentWidget);
2103       if (mParentWidget) {
2104         // GetParentWidget AddRefs, but mParentWidget is weak
2105         mParentWidget->Release();
2106       }
2107     }
2108 
2109     nsView* containerView = FindContainerView();
2110 
2111     nsresult rv = CreateDeviceContext(containerView);
2112     NS_ENSURE_SUCCESS(rv, rv);
2113 
2114     // Create presentation context
2115     NS_ASSERTION(!mPresContext,
2116                  "Shouldn't have a prescontext if we have no shell!");
2117     mPresContext = CreatePresContext(mDocument, nsPresContext::eContext_Galley,
2118                                      containerView);
2119     NS_ENSURE_TRUE(mPresContext, NS_ERROR_OUT_OF_MEMORY);
2120 
2121     rv = mPresContext->Init(mDeviceContext);
2122     if (NS_FAILED(rv)) {
2123       mPresContext = nullptr;
2124       return rv;
2125     }
2126 
2127     rv = MakeWindow(nsSize(mPresContext->DevPixelsToAppUnits(mBounds.width),
2128                            mPresContext->DevPixelsToAppUnits(mBounds.height)),
2129                     containerView);
2130     if (NS_FAILED(rv)) return rv;
2131 
2132     if (mPresContext) {
2133       Hide();
2134 
2135       rv = InitPresentationStuff(mDocument->MayStartLayout());
2136     }
2137 
2138     // If we get here the document load has already started and the
2139     // window is shown because some JS on the page caused it to be
2140     // shown...
2141 
2142     if (mPresShell) {
2143       RefPtr<PresShell> presShell = mPresShell;  // bug 378682
2144       presShell->UnsuppressPainting();
2145     }
2146   }
2147 
2148   // Notify observers that a new page has been shown. This will get run
2149   // from the event loop after we actually draw the page.
2150   RefPtr<nsDocumentShownDispatcher> event =
2151       new nsDocumentShownDispatcher(document);
2152   document->Dispatch(TaskCategory::Other, event.forget());
2153 
2154   return NS_OK;
2155 }
2156 
2157 NS_IMETHODIMP
Hide()2158 nsDocumentViewer::Hide() {
2159   if (!mAttachedToParent && mWindow) {
2160     mWindow->Show(false);
2161   }
2162 
2163   if (!mPresShell) return NS_OK;
2164 
2165   NS_ASSERTION(mPresContext, "Can't have a presshell and no prescontext!");
2166 
2167   // Avoid leaking the old viewer.
2168   if (mPreviousViewer) {
2169     mPreviousViewer->Destroy();
2170     mPreviousViewer = nullptr;
2171   }
2172 
2173   if (mIsSticky) {
2174     // This window is sticky, that means that it might be shown again
2175     // and we don't want the presshell n' all that to be thrown away
2176     // just because the window is hidden.
2177 
2178     return NS_OK;
2179   }
2180 
2181   nsCOMPtr<nsIDocShell> docShell(mContainer);
2182   if (docShell) {
2183 #ifdef DEBUG
2184     nsCOMPtr<nsIContentViewer> currentViewer;
2185     docShell->GetContentViewer(getter_AddRefs(currentViewer));
2186     MOZ_ASSERT(currentViewer == this);
2187 #endif
2188     nsCOMPtr<nsILayoutHistoryState> layoutState;
2189     mPresShell->CaptureHistoryState(getter_AddRefs(layoutState));
2190   }
2191 
2192   // Do not run ScriptRunners queued by DestroyPresShell() in the intermediate
2193   // state before we're done destroying PresShell, PresContext, ViewManager,
2194   // etc.
2195   nsAutoScriptBlocker scriptBlocker;
2196 
2197   DestroyPresShell();
2198 
2199   DestroyPresContext();
2200 
2201   mViewManager = nullptr;
2202   mWindow = nullptr;
2203   mDeviceContext = nullptr;
2204   mParentWidget = nullptr;
2205 
2206   nsCOMPtr<nsIBaseWindow> base_win(mContainer);
2207 
2208   if (base_win && !mAttachedToParent) {
2209     base_win->SetParentWidget(nullptr);
2210   }
2211 
2212   return NS_OK;
2213 }
2214 
2215 NS_IMETHODIMP
GetSticky(bool * aSticky)2216 nsDocumentViewer::GetSticky(bool* aSticky) {
2217   *aSticky = mIsSticky;
2218 
2219   return NS_OK;
2220 }
2221 
2222 NS_IMETHODIMP
SetSticky(bool aSticky)2223 nsDocumentViewer::SetSticky(bool aSticky) {
2224   mIsSticky = aSticky;
2225 
2226   return NS_OK;
2227 }
2228 
2229 NS_IMETHODIMP
ClearHistoryEntry()2230 nsDocumentViewer::ClearHistoryEntry() {
2231   if (mDocument) {
2232     nsJSContext::PokeGC(JS::GCReason::PAGE_HIDE,
2233                         mDocument->GetWrapperPreserveColor(),
2234                         TimeDuration::FromMilliseconds(
2235                             StaticPrefs::javascript_options_gc_delay() * 2));
2236   }
2237 
2238   mSHEntry = nullptr;
2239   return NS_OK;
2240 }
2241 
2242 //-------------------------------------------------------
2243 
MakeWindow(const nsSize & aSize,nsView * aContainerView)2244 nsresult nsDocumentViewer::MakeWindow(const nsSize& aSize,
2245                                       nsView* aContainerView) {
2246   if (GetIsPrintPreview()) {
2247     return NS_OK;
2248   }
2249 
2250   bool shouldAttach = ShouldAttachToTopLevel();
2251 
2252   if (shouldAttach) {
2253     // If the old view is already attached to our parent, detach
2254     DetachFromTopLevelWidget();
2255   }
2256 
2257   mViewManager = new nsViewManager();
2258 
2259   nsDeviceContext* dx = mPresContext->DeviceContext();
2260 
2261   nsresult rv = mViewManager->Init(dx);
2262   if (NS_FAILED(rv)) return rv;
2263 
2264   // The root view is always at 0,0.
2265   nsRect tbounds(nsPoint(0, 0), aSize);
2266   // Create a view
2267   nsView* view = mViewManager->CreateView(tbounds, aContainerView);
2268   if (!view) return NS_ERROR_OUT_OF_MEMORY;
2269 
2270   // Create a widget if we were given a parent widget or don't have a
2271   // container view that we can hook up to without a widget.
2272   // Don't create widgets for ResourceDocs (external resources & svg images),
2273   // because when they're displayed, they're painted into *another* document's
2274   // widget.
2275   if (!mDocument->IsResourceDoc() && (mParentWidget || !aContainerView)) {
2276     // pass in a native widget to be the parent widget ONLY if the view
2277     // hierarchy will stand alone. otherwise the view will find its own parent
2278     // widget and "do the right thing" to establish a parent/child widget
2279     // relationship
2280     nsWidgetInitData initData;
2281     nsWidgetInitData* initDataPtr;
2282     if (!mParentWidget) {
2283       initDataPtr = &initData;
2284       initData.mWindowType = eWindowType_invisible;
2285     } else {
2286       initDataPtr = nullptr;
2287     }
2288 
2289     if (shouldAttach) {
2290       // Reuse the top level parent widget.
2291       rv = view->AttachToTopLevelWidget(mParentWidget);
2292       mAttachedToParent = true;
2293     } else if (!aContainerView && mParentWidget) {
2294       rv = view->CreateWidgetForParent(mParentWidget, initDataPtr, true, false);
2295     } else {
2296       rv = view->CreateWidget(initDataPtr, true, false);
2297     }
2298     if (NS_FAILED(rv)) return rv;
2299   }
2300 
2301   // Setup hierarchical relationship in view manager
2302   mViewManager->SetRootView(view);
2303 
2304   mWindow = view->GetWidget();
2305 
2306   // This SetFocus is necessary so the Arrow Key and Page Key events
2307   // go to the scrolled view as soon as the Window is created instead of going
2308   // to the browser window (this enables keyboard scrolling of the document)
2309   // mWindow->SetFocus();
2310 
2311   return rv;
2312 }
2313 
DetachFromTopLevelWidget()2314 void nsDocumentViewer::DetachFromTopLevelWidget() {
2315   if (mViewManager) {
2316     nsView* oldView = mViewManager->GetRootView();
2317     if (oldView && oldView->IsAttachedToTopLevel()) {
2318       oldView->DetachFromTopLevelWidget();
2319     }
2320   }
2321   mAttachedToParent = false;
2322 }
2323 
FindContainerView()2324 nsView* nsDocumentViewer::FindContainerView() {
2325   if (!mContainer) {
2326     return nullptr;
2327   }
2328 
2329   nsCOMPtr<nsIDocShell> docShell(mContainer);
2330   nsCOMPtr<nsPIDOMWindowOuter> pwin(docShell->GetWindow());
2331   if (!pwin) {
2332     return nullptr;
2333   }
2334 
2335   nsCOMPtr<Element> containerElement = pwin->GetFrameElementInternal();
2336   if (!containerElement) {
2337     return nullptr;
2338   }
2339 
2340   nsIFrame* subdocFrame = containerElement->GetPrimaryFrame();
2341   if (!subdocFrame) {
2342     // XXX Silenced by default in bug 1175289
2343     LAYOUT_WARNING("Subdocument container has no frame");
2344     return nullptr;
2345   }
2346 
2347   // Check subdocFrame just to be safe. If this somehow fails we treat that as
2348   // display:none, the document is not displayed.
2349   if (!subdocFrame->IsSubDocumentFrame()) {
2350     NS_WARNING_ASSERTION(subdocFrame->Type() == LayoutFrameType::None,
2351                          "Subdocument container has non-subdocument frame");
2352     return nullptr;
2353   }
2354 
2355   NS_ASSERTION(subdocFrame->GetView(), "Subdoc frames must have views");
2356   return static_cast<nsSubDocumentFrame*>(subdocFrame)->EnsureInnerView();
2357 }
2358 
CreateDeviceContext(nsView * aContainerView)2359 nsresult nsDocumentViewer::CreateDeviceContext(nsView* aContainerView) {
2360   MOZ_ASSERT(!mPresShell && !mWindow,
2361              "This will screw up our existing presentation");
2362   MOZ_ASSERT(mDocument, "Gotta have a document here");
2363 
2364   Document* doc = mDocument->GetDisplayDocument();
2365   if (doc) {
2366     NS_ASSERTION(!aContainerView,
2367                  "External resource document embedded somewhere?");
2368     // We want to use our display document's device context if possible
2369     nsPresContext* ctx = doc->GetPresContext();
2370     if (ctx) {
2371       mDeviceContext = ctx->DeviceContext();
2372       return NS_OK;
2373     }
2374   }
2375 
2376   // Create a device context even if we already have one, since our widget
2377   // might have changed.
2378   nsIWidget* widget = nullptr;
2379   if (aContainerView) {
2380     widget = aContainerView->GetNearestWidget(nullptr);
2381   }
2382   if (!widget) {
2383     widget = mParentWidget;
2384   }
2385   if (widget) {
2386     widget = widget->GetTopLevelWidget();
2387   }
2388 
2389   mDeviceContext = new nsDeviceContext();
2390   mDeviceContext->Init(widget);
2391   return NS_OK;
2392 }
2393 
2394 // Return the selection for the document. Note that text fields have their
2395 // own selection, which cannot be accessed with this method.
GetDocumentSelection()2396 mozilla::dom::Selection* nsDocumentViewer::GetDocumentSelection() {
2397   if (!mPresShell) {
2398     return nullptr;
2399   }
2400 
2401   return mPresShell->GetCurrentSelection(SelectionType::eNormal);
2402 }
2403 
2404 /* ============================================================================
2405  * nsIContentViewerEdit
2406  * ============================================================================
2407  */
2408 
ClearSelection()2409 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP nsDocumentViewer::ClearSelection() {
2410   // use nsCopySupport::GetSelectionForCopy() ?
2411   RefPtr<mozilla::dom::Selection> selection = GetDocumentSelection();
2412   if (!selection) {
2413     return NS_ERROR_FAILURE;
2414   }
2415 
2416   ErrorResult rv;
2417   selection->CollapseToStart(rv);
2418   return rv.StealNSResult();
2419 }
2420 
SelectAll()2421 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP nsDocumentViewer::SelectAll() {
2422   // XXX this is a temporary implementation copied from nsWebShell
2423   // for now. I think Document and friends should have some helper
2424   // functions to make this easier.
2425 
2426   // use nsCopySupport::GetSelectionForCopy() ?
2427   RefPtr<mozilla::dom::Selection> selection = GetDocumentSelection();
2428   if (!selection) {
2429     return NS_ERROR_FAILURE;
2430   }
2431 
2432   if (!mDocument) {
2433     return NS_ERROR_FAILURE;
2434   }
2435 
2436   nsCOMPtr<nsINode> bodyNode;
2437   if (mDocument->IsHTMLOrXHTML()) {
2438     // XXXbz why not just do GetBody() for all documents, then GetRootElement()
2439     // if GetBody() is null?
2440     bodyNode = mDocument->GetBody();
2441   } else {
2442     bodyNode = mDocument->GetRootElement();
2443   }
2444   if (!bodyNode) return NS_ERROR_FAILURE;
2445 
2446   ErrorResult err;
2447   selection->RemoveAllRanges(err);
2448   if (err.Failed()) {
2449     return err.StealNSResult();
2450   }
2451 
2452   mozilla::dom::Selection::AutoUserInitiated userSelection(selection);
2453   selection->SelectAllChildren(*bodyNode, err);
2454   return err.StealNSResult();
2455 }
2456 
CopySelection()2457 NS_IMETHODIMP nsDocumentViewer::CopySelection() {
2458   RefPtr<PresShell> presShell = mPresShell;
2459   nsCopySupport::FireClipboardEvent(eCopy, nsIClipboard::kGlobalClipboard,
2460                                     presShell, nullptr);
2461   return NS_OK;
2462 }
2463 
CopyLinkLocation()2464 NS_IMETHODIMP nsDocumentViewer::CopyLinkLocation() {
2465   NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_INITIALIZED);
2466   nsCOMPtr<nsINode> node = GetPopupLinkNode();
2467   // make noise if we're not in a link
2468   NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
2469 
2470   nsCOMPtr<dom::Element> elm(do_QueryInterface(node));
2471   NS_ENSURE_TRUE(elm, NS_ERROR_FAILURE);
2472 
2473   nsAutoString locationText;
2474   nsContentUtils::GetLinkLocation(elm, locationText);
2475   if (locationText.IsEmpty()) return NS_ERROR_FAILURE;
2476 
2477   nsresult rv = NS_OK;
2478   nsCOMPtr<nsIClipboardHelper> clipboard(
2479       do_GetService("@mozilla.org/widget/clipboardhelper;1", &rv));
2480   NS_ENSURE_SUCCESS(rv, rv);
2481 
2482   // copy the href onto the clipboard
2483   return clipboard->CopyString(locationText);
2484 }
2485 
CopyImage(int32_t aCopyFlags)2486 NS_IMETHODIMP nsDocumentViewer::CopyImage(int32_t aCopyFlags) {
2487   NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_INITIALIZED);
2488   nsCOMPtr<nsIImageLoadingContent> node = GetPopupImageNode();
2489   // make noise if we're not in an image
2490   NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
2491 
2492   nsCOMPtr<nsILoadContext> loadContext(mContainer);
2493   return nsCopySupport::ImageCopy(node, loadContext, aCopyFlags);
2494 }
2495 
GetCopyable(bool * aCopyable)2496 NS_IMETHODIMP nsDocumentViewer::GetCopyable(bool* aCopyable) {
2497   NS_ENSURE_ARG_POINTER(aCopyable);
2498   *aCopyable = nsCopySupport::CanCopy(mDocument);
2499   return NS_OK;
2500 }
2501 
GetContents(const char * mimeType,bool selectionOnly,nsAString & aOutValue)2502 NS_IMETHODIMP nsDocumentViewer::GetContents(const char* mimeType,
2503                                             bool selectionOnly,
2504                                             nsAString& aOutValue) {
2505   aOutValue.Truncate();
2506 
2507   NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_INITIALIZED);
2508   NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_INITIALIZED);
2509 
2510   // Now we have the selection.  Make sure it's nonzero:
2511   RefPtr<Selection> sel;
2512   if (selectionOnly) {
2513     sel = nsCopySupport::GetSelectionForCopy(mDocument);
2514     NS_ENSURE_TRUE(sel, NS_ERROR_FAILURE);
2515 
2516     if (sel->IsCollapsed()) {
2517       return NS_OK;
2518     }
2519   }
2520 
2521   // call the copy code
2522   return nsCopySupport::GetContents(nsDependentCString(mimeType), 0, sel,
2523                                     mDocument, aOutValue);
2524 }
2525 
GetCanGetContents(bool * aCanGetContents)2526 NS_IMETHODIMP nsDocumentViewer::GetCanGetContents(bool* aCanGetContents) {
2527   NS_ENSURE_ARG_POINTER(aCanGetContents);
2528   *aCanGetContents = false;
2529   NS_ENSURE_STATE(mDocument);
2530   *aCanGetContents = nsCopySupport::CanCopy(mDocument);
2531   return NS_OK;
2532 }
2533 
SetCommandNode(nsINode * aNode)2534 NS_IMETHODIMP nsDocumentViewer::SetCommandNode(nsINode* aNode) {
2535   Document* document = GetDocument();
2536   NS_ENSURE_STATE(document);
2537 
2538   nsCOMPtr<nsPIDOMWindowOuter> window(document->GetWindow());
2539   NS_ENSURE_TRUE(window, NS_ERROR_NOT_AVAILABLE);
2540 
2541   nsCOMPtr<nsPIWindowRoot> root = window->GetTopWindowRoot();
2542   NS_ENSURE_STATE(root);
2543 
2544   root->SetPopupNode(aNode);
2545   return NS_OK;
2546 }
2547 
2548 NS_IMETHODIMP
GetDeviceFullZoomForTest(float * aDeviceFullZoom)2549 nsDocumentViewer::GetDeviceFullZoomForTest(float* aDeviceFullZoom) {
2550   NS_ENSURE_ARG_POINTER(aDeviceFullZoom);
2551   nsPresContext* pc = GetPresContext();
2552   *aDeviceFullZoom = pc ? pc->GetDeviceFullZoom() : 1.0;
2553   return NS_OK;
2554 }
2555 
2556 NS_IMETHODIMP
SetAuthorStyleDisabled(bool aStyleDisabled)2557 nsDocumentViewer::SetAuthorStyleDisabled(bool aStyleDisabled) {
2558   if (mPresShell) {
2559     mPresShell->SetAuthorStyleDisabled(aStyleDisabled);
2560   }
2561   return NS_OK;
2562 }
2563 
2564 NS_IMETHODIMP
GetAuthorStyleDisabled(bool * aStyleDisabled)2565 nsDocumentViewer::GetAuthorStyleDisabled(bool* aStyleDisabled) {
2566   if (mPresShell) {
2567     *aStyleDisabled = mPresShell->GetAuthorStyleDisabled();
2568   } else {
2569     *aStyleDisabled = false;
2570   }
2571   return NS_OK;
2572 }
2573 
2574 /* [noscript,notxpcom] Encoding getHintCharset (); */
NS_IMETHODIMP_(const Encoding *)2575 NS_IMETHODIMP_(const Encoding*)
2576 nsDocumentViewer::GetReloadEncodingAndSource(int32_t* aSource) {
2577   *aSource = mReloadEncodingSource;
2578   if (kCharsetUninitialized == mReloadEncodingSource) {
2579     return nullptr;
2580   }
2581   return mReloadEncoding;
2582 }
2583 
NS_IMETHODIMP_(void)2584 NS_IMETHODIMP_(void)
2585 nsDocumentViewer::SetReloadEncodingAndSource(const Encoding* aEncoding,
2586                                              int32_t aSource) {
2587   MOZ_ASSERT(
2588       aSource == kCharsetUninitialized ||
2589       (aSource >= kCharsetFromFinalAutoDetectionWouldHaveBeenUTF8 &&
2590        aSource <=
2591            kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD) ||
2592       aSource == kCharsetFromFinalUserForcedAutoDetection);
2593   mReloadEncoding = aEncoding;
2594   mReloadEncodingSource = aSource;
2595 }
2596 
NS_IMETHODIMP_(void)2597 NS_IMETHODIMP_(void)
2598 nsDocumentViewer::ForgetReloadEncoding() {
2599   mReloadEncoding = nullptr;
2600   mReloadEncodingSource = kCharsetUninitialized;
2601 }
2602 
GetContentSizeInternal(int32_t * aWidth,int32_t * aHeight,nscoord aMaxWidth,nscoord aMaxHeight)2603 nsresult nsDocumentViewer::GetContentSizeInternal(int32_t* aWidth,
2604                                                   int32_t* aHeight,
2605                                                   nscoord aMaxWidth,
2606                                                   nscoord aMaxHeight) {
2607   NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
2608 
2609   RefPtr<PresShell> presShell = GetPresShell();
2610   NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
2611 
2612   // Flush out all content and style updates. We can't use a resize reflow
2613   // because it won't change some sizes that a style change reflow will.
2614   mDocument->FlushPendingNotifications(FlushType::Layout);
2615 
2616   nsIFrame* root = presShell->GetRootFrame();
2617   NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
2618 
2619   WritingMode wm = root->GetWritingMode();
2620 
2621   nscoord prefISize;
2622   {
2623     RefPtr<gfxContext> rcx(presShell->CreateReferenceRenderingContext());
2624     nscoord maxISize = wm.IsVertical() ? aMaxHeight : aMaxWidth;
2625     prefISize = std::min(root->GetPrefISize(rcx), maxISize);
2626   }
2627 
2628   // We should never intentionally get here with this sentinel value, but it's
2629   // possible that a document with huge sizes might inadvertently have a
2630   // prefISize that exactly matches NS_UNCONSTRAINEDSIZE.
2631   // Just bail if that happens.
2632   NS_ENSURE_TRUE(prefISize != NS_UNCONSTRAINEDSIZE, NS_ERROR_FAILURE);
2633 
2634   nscoord height = wm.IsVertical() ? prefISize : aMaxHeight;
2635   nscoord width = wm.IsVertical() ? aMaxWidth : prefISize;
2636   nsresult rv =
2637       presShell->ResizeReflow(width, height, ResizeReflowOptions::BSizeLimit);
2638   NS_ENSURE_SUCCESS(rv, rv);
2639 
2640   RefPtr<nsPresContext> presContext = GetPresContext();
2641   NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
2642 
2643   // Protect against bogus returns here
2644   nsRect shellArea = presContext->GetVisibleArea();
2645   NS_ENSURE_TRUE(shellArea.width != NS_UNCONSTRAINEDSIZE &&
2646                      shellArea.height != NS_UNCONSTRAINEDSIZE,
2647                  NS_ERROR_FAILURE);
2648 
2649   // Ceil instead of rounding here, so we can actually guarantee showing all the
2650   // content.
2651   *aWidth = std::ceil(presContext->AppUnitsToFloatDevPixels(shellArea.width));
2652   *aHeight = std::ceil(presContext->AppUnitsToFloatDevPixels(shellArea.height));
2653 
2654   return NS_OK;
2655 }
2656 
2657 NS_IMETHODIMP
GetContentSize(int32_t * aWidth,int32_t * aHeight)2658 nsDocumentViewer::GetContentSize(int32_t* aWidth, int32_t* aHeight) {
2659   NS_ENSURE_TRUE(mContainer, NS_ERROR_NOT_AVAILABLE);
2660 
2661   RefPtr<BrowsingContext> bc = mContainer->GetBrowsingContext();
2662   NS_ENSURE_TRUE(bc, NS_ERROR_NOT_AVAILABLE);
2663 
2664   // It's only valid to access this from a top frame.  Doesn't work from
2665   // sub-frames.
2666   NS_ENSURE_TRUE(bc->IsTop(), NS_ERROR_FAILURE);
2667 
2668   return GetContentSizeInternal(aWidth, aHeight, NS_UNCONSTRAINEDSIZE,
2669                                 NS_UNCONSTRAINEDSIZE);
2670 }
2671 
2672 NS_IMETHODIMP
GetContentSizeConstrained(int32_t aMaxWidth,int32_t aMaxHeight,int32_t * aWidth,int32_t * aHeight)2673 nsDocumentViewer::GetContentSizeConstrained(int32_t aMaxWidth,
2674                                             int32_t aMaxHeight, int32_t* aWidth,
2675                                             int32_t* aHeight) {
2676   RefPtr<nsPresContext> presContext = GetPresContext();
2677   NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
2678 
2679   nscoord maxWidth = NS_UNCONSTRAINEDSIZE;
2680   nscoord maxHeight = NS_UNCONSTRAINEDSIZE;
2681   if (aMaxWidth > 0) {
2682     maxWidth = presContext->DevPixelsToAppUnits(aMaxWidth);
2683   }
2684   if (aMaxHeight > 0) {
2685     maxHeight = presContext->DevPixelsToAppUnits(aMaxHeight);
2686   }
2687 
2688   return GetContentSizeInternal(aWidth, aHeight, maxWidth, maxHeight);
2689 }
2690 
NS_IMPL_ISUPPORTS(nsDocViewerSelectionListener,nsISelectionListener)2691 NS_IMPL_ISUPPORTS(nsDocViewerSelectionListener, nsISelectionListener)
2692 
2693 /*
2694  * GetPopupNode, GetPopupLinkNode and GetPopupImageNode are helpers
2695  * for the cmd_copyLink / cmd_copyImageLocation / cmd_copyImageContents family
2696  * of commands. The focus controller stores the popup node, these retrieve
2697  * them and munge appropriately. Note that we have to store the popup node
2698  * rather than retrieving it from EventStateManager::GetFocusedContent because
2699  * not all content (images included) can receive focus.
2700  */
2701 
2702 already_AddRefed<nsINode> nsDocumentViewer::GetPopupNode() {
2703   // get the document
2704   Document* document = GetDocument();
2705   NS_ENSURE_TRUE(document, nullptr);
2706 
2707   // get the private dom window
2708   nsCOMPtr<nsPIDOMWindowOuter> window(document->GetWindow());
2709   NS_ENSURE_TRUE(window, nullptr);
2710   if (window) {
2711     nsCOMPtr<nsPIWindowRoot> root = window->GetTopWindowRoot();
2712     NS_ENSURE_TRUE(root, nullptr);
2713 
2714     // get the popup node
2715     nsCOMPtr<nsINode> node = root->GetPopupNode();
2716     if (!node) {
2717       nsPIDOMWindowOuter* rootWindow = root->GetWindow();
2718       if (rootWindow) {
2719         nsCOMPtr<Document> rootDoc = rootWindow->GetExtantDoc();
2720         if (rootDoc) {
2721           nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
2722           if (pm) {
2723             node = pm->GetLastTriggerPopupNode(rootDoc);
2724           }
2725         }
2726       }
2727     }
2728     return node.forget();
2729   }
2730 
2731   return nullptr;
2732 }
2733 
2734 // GetPopupLinkNode: return popup link node or fail
GetPopupLinkNode()2735 already_AddRefed<nsINode> nsDocumentViewer::GetPopupLinkNode() {
2736   // find popup node
2737   nsCOMPtr<nsINode> node = GetPopupNode();
2738 
2739   // find out if we have a link in our ancestry
2740   while (node) {
2741     nsCOMPtr<nsIContent> content(do_QueryInterface(node));
2742     if (content) {
2743       nsCOMPtr<nsIURI> hrefURI = content->GetHrefURI();
2744       if (hrefURI) {
2745         return node.forget();
2746       }
2747     }
2748 
2749     // get our parent and keep trying...
2750     node = node->GetParentNode();
2751   }
2752 
2753   // if we have no node, fail
2754   return nullptr;
2755 }
2756 
2757 // GetPopupLinkNode: return popup image node or fail
GetPopupImageNode()2758 already_AddRefed<nsIImageLoadingContent> nsDocumentViewer::GetPopupImageNode() {
2759   // find popup node
2760   nsCOMPtr<nsINode> node = GetPopupNode();
2761   nsCOMPtr<nsIImageLoadingContent> img = do_QueryInterface(node);
2762   return img.forget();
2763 }
2764 
2765 /*
2766  * XXX dr
2767  * ------
2768  * These two functions -- GetInLink and GetInImage -- are kind of annoying
2769  * in that they only get called from the controller (in
2770  * nsDOMWindowController::IsCommandEnabled). The actual construction of the
2771  * context menus in communicator (nsContextMenu.js) has its own, redundant
2772  * tests. No big deal, but good to keep in mind if we ever clean context
2773  * menus.
2774  */
2775 
GetInLink(bool * aInLink)2776 NS_IMETHODIMP nsDocumentViewer::GetInLink(bool* aInLink) {
2777 #ifdef DEBUG_dr
2778   printf("dr :: nsDocumentViewer::GetInLink\n");
2779 #endif
2780 
2781   NS_ENSURE_ARG_POINTER(aInLink);
2782 
2783   // we're not in a link unless i say so
2784   *aInLink = false;
2785 
2786   // get the popup link
2787   nsCOMPtr<nsINode> node = GetPopupLinkNode();
2788   if (!node) {
2789     return NS_ERROR_FAILURE;
2790   }
2791 
2792   // if we made it here, we're in a link
2793   *aInLink = true;
2794   return NS_OK;
2795 }
2796 
GetInImage(bool * aInImage)2797 NS_IMETHODIMP nsDocumentViewer::GetInImage(bool* aInImage) {
2798 #ifdef DEBUG_dr
2799   printf("dr :: nsDocumentViewer::GetInImage\n");
2800 #endif
2801 
2802   NS_ENSURE_ARG_POINTER(aInImage);
2803 
2804   // we're not in an image unless i say so
2805   *aInImage = false;
2806 
2807   // get the popup image
2808   nsCOMPtr<nsIImageLoadingContent> node = GetPopupImageNode();
2809   if (!node) {
2810     return NS_ERROR_FAILURE;
2811   }
2812 
2813   // Make sure there is a URI assigned. This allows <input type="image"> to
2814   // be an image but rejects other <input> types. This matches what
2815   // nsContextMenu.js does.
2816   nsCOMPtr<nsIURI> uri;
2817   node->GetCurrentURI(getter_AddRefs(uri));
2818   if (uri) {
2819     // if we made it here, we're in an image
2820     *aInImage = true;
2821   }
2822 
2823   return NS_OK;
2824 }
2825 
NotifySelectionChanged(Document *,Selection *,int16_t aReason)2826 NS_IMETHODIMP nsDocViewerSelectionListener::NotifySelectionChanged(
2827     Document*, Selection*, int16_t aReason) {
2828   if (!mDocViewer) {
2829     return NS_OK;
2830   }
2831 
2832   // get the selection state
2833   RefPtr<mozilla::dom::Selection> selection =
2834       mDocViewer->GetDocumentSelection();
2835   if (!selection) {
2836     return NS_ERROR_FAILURE;
2837   }
2838 
2839   Document* theDoc = mDocViewer->GetDocument();
2840   if (!theDoc) return NS_ERROR_FAILURE;
2841 
2842   nsCOMPtr<nsPIDOMWindowOuter> domWindow = theDoc->GetWindow();
2843   if (!domWindow) return NS_ERROR_FAILURE;
2844 
2845   bool selectionCollapsed = selection->IsCollapsed();
2846   // We only call UpdateCommands when the selection changes from collapsed to
2847   // non-collapsed or vice versa, however we skip the initializing collapse. We
2848   // might need another update string for simple selection changes, but that
2849   // would be expenseive.
2850   if (mSelectionWasCollapsed != selectionCollapsed) {
2851     domWindow->UpdateCommands(u"select"_ns, selection, aReason);
2852     mSelectionWasCollapsed = selectionCollapsed;
2853   }
2854 
2855   return NS_OK;
2856 }
2857 
2858 // nsDocViewerFocusListener
NS_IMPL_ISUPPORTS(nsDocViewerFocusListener,nsIDOMEventListener)2859 NS_IMPL_ISUPPORTS(nsDocViewerFocusListener, nsIDOMEventListener)
2860 
2861 nsresult nsDocViewerFocusListener::HandleEvent(Event* aEvent) {
2862   NS_ENSURE_STATE(mDocViewer);
2863 
2864   RefPtr<PresShell> presShell = mDocViewer->GetPresShell();
2865   NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
2866 
2867   RefPtr<nsFrameSelection> selection =
2868       presShell->GetLastFocusedFrameSelection();
2869   NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
2870   auto selectionStatus = selection->GetDisplaySelection();
2871   nsAutoString eventType;
2872   aEvent->GetType(eventType);
2873   if (eventType.EqualsLiteral("focus")) {
2874     // If selection was disabled, re-enable it.
2875     if (selectionStatus == nsISelectionController::SELECTION_DISABLED ||
2876         selectionStatus == nsISelectionController::SELECTION_HIDDEN) {
2877       selection->SetDisplaySelection(nsISelectionController::SELECTION_ON);
2878       selection->RepaintSelection(SelectionType::eNormal);
2879     }
2880   } else {
2881     MOZ_ASSERT(eventType.EqualsLiteral("blur"), "Unexpected event type");
2882     // If selection was on, disable it.
2883     if (selectionStatus == nsISelectionController::SELECTION_ON ||
2884         selectionStatus == nsISelectionController::SELECTION_ATTENTION) {
2885       selection->SetDisplaySelection(
2886           nsISelectionController::SELECTION_DISABLED);
2887       selection->RepaintSelection(SelectionType::eNormal);
2888     }
2889   }
2890 
2891   return NS_OK;
2892 }
2893 
2894 /** ---------------------------------------------------
2895  *  From nsIWebBrowserPrint
2896  */
2897 
2898 #ifdef NS_PRINTING
2899 
2900 NS_IMETHODIMP
Print(nsIPrintSettings * aPrintSettings,nsIWebProgressListener * aWebProgressListener)2901 nsDocumentViewer::Print(nsIPrintSettings* aPrintSettings,
2902                         nsIWebProgressListener* aWebProgressListener) {
2903   if (NS_WARN_IF(!mContainer)) {
2904     PR_PL(("Container was destroyed yet we are still trying to use it!"));
2905     return NS_ERROR_FAILURE;
2906   }
2907 
2908   if (NS_WARN_IF(!mDocument) || NS_WARN_IF(!mDeviceContext)) {
2909     PR_PL(("Can't Print without a document and a device context"));
2910     return NS_ERROR_FAILURE;
2911   }
2912 
2913   if (NS_WARN_IF(mPrintJob && mPrintJob->GetIsPrinting())) {
2914     // If we are printing another URL, then exit.
2915     // The reason we check here is because this method can be called while
2916     // another is still in here (the printing dialog is a good example).  the
2917     // only time we can print more than one job at a time is the regression
2918     // tests.
2919     nsresult rv = NS_ERROR_NOT_AVAILABLE;
2920     RefPtr<nsPrintJob>(mPrintJob)->FirePrintingErrorEvent(rv);
2921     return rv;
2922   }
2923 
2924   OnDonePrinting();
2925   RefPtr<nsPrintJob> printJob = new nsPrintJob();
2926   nsresult rv =
2927       printJob->Initialize(this, mContainer, mDocument,
2928                            float(AppUnitsPerCSSInch()) /
2929                                float(mDeviceContext->AppUnitsPerDevPixel()));
2930   if (NS_WARN_IF(NS_FAILED(rv))) {
2931     printJob->Destroy();
2932     return rv;
2933   }
2934 
2935   mPrintJob = printJob;
2936   rv = printJob->Print(mDocument, aPrintSettings, aWebProgressListener);
2937   if (NS_WARN_IF(NS_FAILED(rv))) {
2938     OnDonePrinting();
2939   }
2940   return rv;
2941 }
2942 
2943 NS_IMETHODIMP
PrintPreview(nsIPrintSettings * aPrintSettings,nsIWebProgressListener * aWebProgressListener,PrintPreviewResolver && aCallback)2944 nsDocumentViewer::PrintPreview(nsIPrintSettings* aPrintSettings,
2945                                nsIWebProgressListener* aWebProgressListener,
2946                                PrintPreviewResolver&& aCallback) {
2947 #  ifdef NS_PRINT_PREVIEW
2948   RefPtr<Document> doc = mDocument.get();
2949   NS_ENSURE_STATE(doc);
2950 
2951   if (NS_WARN_IF(GetIsPrinting())) {
2952     return NS_ERROR_FAILURE;
2953   }
2954 
2955   nsCOMPtr<nsIDocShell> docShell(mContainer);
2956   if (NS_WARN_IF(!docShell) || NS_WARN_IF(!mDeviceContext)) {
2957     PR_PL(("Can't Print Preview without device context and docshell"));
2958     return NS_ERROR_FAILURE;
2959   }
2960 
2961   NS_ENSURE_STATE(!GetIsPrinting());
2962   // beforeprint event may have caused ContentViewer to be shutdown.
2963   NS_ENSURE_STATE(mContainer);
2964   NS_ENSURE_STATE(mDeviceContext);
2965 
2966   OnDonePrinting();
2967 
2968   RefPtr<nsPrintJob> printJob = new nsPrintJob();
2969 
2970   nsresult rv =
2971       printJob->Initialize(this, mContainer, doc,
2972                            float(AppUnitsPerCSSInch()) /
2973                                float(mDeviceContext->AppUnitsPerDevPixel()));
2974   if (NS_WARN_IF(NS_FAILED(rv))) {
2975     printJob->Destroy();
2976     return rv;
2977   }
2978   mPrintJob = printJob;
2979 
2980   rv = printJob->PrintPreview(doc, aPrintSettings, aWebProgressListener,
2981                               std::move(aCallback));
2982   if (NS_WARN_IF(NS_FAILED(rv))) {
2983     OnDonePrinting();
2984   }
2985   return rv;
2986 #  else
2987   return NS_ERROR_FAILURE;
2988 #  endif  // NS_PRINT_PREVIEW
2989 }
2990 
GetTargetPageFrame(int32_t aTargetPageNum,nsPageSequenceFrame * aSequenceFrame)2991 static const nsIFrame* GetTargetPageFrame(int32_t aTargetPageNum,
2992                                           nsPageSequenceFrame* aSequenceFrame) {
2993   MOZ_ASSERT(aTargetPageNum > 0 &&
2994              aTargetPageNum <=
2995                  aSequenceFrame->PrincipalChildList().GetLength());
2996   return aSequenceFrame->PrincipalChildList().FrameAt(aTargetPageNum - 1);
2997 }
2998 
2999 // Calculate the scroll position where the center of |aFrame| is positioned at
3000 // the center of |aScrollable|'s scroll port for the print preview.
3001 // So what we do for that is;
3002 // 1) Calculate the position of the center of |aFrame| in the print preview
3003 //    coordinates.
3004 // 2) Reduce the half height of the scroll port from the result of 1.
ScrollPositionForFrame(const nsIFrame * aFrame,nsIScrollableFrame * aScrollable,float aPreviewScale)3005 static nscoord ScrollPositionForFrame(const nsIFrame* aFrame,
3006                                       nsIScrollableFrame* aScrollable,
3007                                       float aPreviewScale) {
3008   // Note that even if the computed scroll position is out of the range of
3009   // the scroll port, it gets clamped in nsIScrollableFrame::ScrollTo.
3010   return nscoord(aPreviewScale * aFrame->GetRect().Center().y -
3011                  float(aScrollable->GetScrollPortRect().height) / 2.0f);
3012 }
3013 
3014 //----------------------------------------------------------------------
3015 NS_IMETHODIMP
PrintPreviewScrollToPage(int16_t aType,int32_t aPageNum)3016 nsDocumentViewer::PrintPreviewScrollToPage(int16_t aType, int32_t aPageNum) {
3017   if (!GetIsPrintPreview() || mPrintJob->GetIsCreatingPrintPreview())
3018     return NS_ERROR_FAILURE;
3019 
3020   nsIScrollableFrame* sf =
3021       mPrintJob->GetPrintPreviewPresShell()->GetRootScrollFrameAsScrollable();
3022   if (!sf) return NS_OK;
3023 
3024   auto [seqFrame, sheetCount] = mPrintJob->GetSeqFrameAndCountSheets();
3025   Unused << sheetCount;
3026   if (!seqFrame) {
3027     return NS_ERROR_FAILURE;
3028   }
3029 
3030   float previewScale = seqFrame->GetPrintPreviewScale();
3031 
3032   nsPoint dest = sf->GetScrollPosition();
3033 
3034   switch (aType) {
3035     case nsIWebBrowserPrint::PRINTPREVIEW_HOME:
3036       dest.y = 0;
3037       break;
3038     case nsIWebBrowserPrint::PRINTPREVIEW_END:
3039       dest.y = sf->GetScrollRange().YMost();
3040       break;
3041     case nsIWebBrowserPrint::PRINTPREVIEW_PREV_PAGE:
3042     case nsIWebBrowserPrint::PRINTPREVIEW_NEXT_PAGE: {
3043       auto [currentFrame, currentSheetNumber] = GetCurrentSheetFrameAndNumber();
3044       Unused << currentSheetNumber;
3045       if (!currentFrame) {
3046         return NS_OK;
3047       }
3048 
3049       const nsIFrame* targetFrame = nullptr;
3050       if (aType == nsIWebBrowserPrint::PRINTPREVIEW_PREV_PAGE) {
3051         targetFrame = currentFrame->GetPrevInFlow();
3052       } else {
3053         targetFrame = currentFrame->GetNextInFlow();
3054       }
3055       if (!targetFrame) {
3056         return NS_OK;
3057       }
3058 
3059       dest.y = ScrollPositionForFrame(targetFrame, sf, previewScale);
3060       break;
3061     }
3062     case nsIWebBrowserPrint::PRINTPREVIEW_GOTO_PAGENUM: {
3063       if (aPageNum <= 0 || aPageNum > sheetCount) {
3064         return NS_ERROR_INVALID_ARG;
3065       }
3066 
3067       const nsIFrame* targetFrame = GetTargetPageFrame(aPageNum, seqFrame);
3068       MOZ_ASSERT(targetFrame);
3069 
3070       dest.y = ScrollPositionForFrame(targetFrame, sf, previewScale);
3071       break;
3072     }
3073     default:
3074       return NS_ERROR_INVALID_ARG;
3075       break;
3076   }
3077 
3078   sf->ScrollTo(dest, ScrollMode::Instant);
3079 
3080   return NS_OK;
3081 }
3082 
3083 std::tuple<const nsIFrame*, int32_t>
GetCurrentSheetFrameAndNumber() const3084 nsDocumentViewer::GetCurrentSheetFrameAndNumber() const {
3085   MOZ_ASSERT(mPrintJob);
3086   MOZ_ASSERT(GetIsPrintPreview() && !mPrintJob->GetIsCreatingPrintPreview());
3087 
3088   // in PP mPrtPreview->mPrintObject->mSeqFrame is null
3089   auto [seqFrame, sheetCount] = mPrintJob->GetSeqFrameAndCountSheets();
3090   Unused << sheetCount;
3091   if (!seqFrame) {
3092     return {nullptr, 0};
3093   }
3094 
3095   nsIScrollableFrame* sf =
3096       mPrintJob->GetPrintPreviewPresShell()->GetRootScrollFrameAsScrollable();
3097   if (!sf) {
3098     // No scrollable contents, returns 1 even if there are multiple sheets.
3099     return {seqFrame->PrincipalChildList().FirstChild(), 1};
3100   }
3101 
3102   nsPoint currentScrollPosition = sf->GetScrollPosition();
3103   float halfwayPoint =
3104       currentScrollPosition.y + float(sf->GetScrollPortRect().height) / 2.0f;
3105   float lastDistanceFromHalfwayPoint = std::numeric_limits<float>::max();
3106   int32_t sheetNumber = 0;
3107   const nsIFrame* currentSheet = nullptr;
3108   float previewScale = seqFrame->GetPrintPreviewScale();
3109   for (const nsIFrame* sheetFrame : seqFrame->PrincipalChildList()) {
3110     nsRect sheetRect = sheetFrame->GetRect();
3111     sheetNumber++;
3112     currentSheet = sheetFrame;
3113 
3114     float bottomOfSheet = sheetRect.YMost() * previewScale;
3115     if (bottomOfSheet < halfwayPoint) {
3116       // If the bottom of the sheet is not yet over the halfway point, iterate
3117       // the next frame to see if the next frame is over the halfway point and
3118       // compare the distance from the halfway point.
3119       lastDistanceFromHalfwayPoint = halfwayPoint - bottomOfSheet;
3120       continue;
3121     }
3122 
3123     float topOfSheet = sheetRect.Y() * previewScale;
3124     if (topOfSheet <= halfwayPoint) {
3125       // If the top of the sheet is not yet over the halfway point or on the
3126       // point, it's the current sheet.
3127       break;
3128     }
3129 
3130     // Now the sheet rect is completely over the halfway point, compare the
3131     // distances from the halfway point.
3132     if ((topOfSheet - halfwayPoint) >= lastDistanceFromHalfwayPoint) {
3133       // If the previous sheet distance is less than or equal to the current
3134       // sheet distance, choose the previous one as the current.
3135       sheetNumber--;
3136       MOZ_ASSERT(sheetNumber > 0);
3137       currentSheet = currentSheet->GetPrevInFlow();
3138       MOZ_ASSERT(currentSheet);
3139     }
3140     break;
3141   }
3142 
3143   MOZ_ASSERT(sheetNumber <= sheetCount);
3144   return {currentSheet, sheetNumber};
3145 }
3146 
3147 // XXXdholbert As noted in nsIWebBrowserPrint.idl, this API (the IDL attr
3148 // 'printPreviewCurrentPageNumber') is misnamed and needs s/Page/Sheet/.  See
3149 // bug 1669762.
3150 NS_IMETHODIMP
GetPrintPreviewCurrentPageNumber(int32_t * aNumber)3151 nsDocumentViewer::GetPrintPreviewCurrentPageNumber(int32_t* aNumber) {
3152   NS_ENSURE_ARG_POINTER(aNumber);
3153   NS_ENSURE_TRUE(mPrintJob, NS_ERROR_FAILURE);
3154   if (!GetIsPrintPreview() || mPrintJob->GetIsCreatingPrintPreview()) {
3155     return NS_ERROR_FAILURE;
3156   }
3157 
3158   auto [currentFrame, currentSheetNumber] = GetCurrentSheetFrameAndNumber();
3159   Unused << currentFrame;
3160   if (!currentSheetNumber) {
3161     return NS_ERROR_FAILURE;
3162   }
3163 
3164   *aNumber = currentSheetNumber;
3165 
3166   return NS_OK;
3167 }
3168 
3169 // XXX This always returns false for subdocuments
3170 NS_IMETHODIMP
GetDoingPrint(bool * aDoingPrint)3171 nsDocumentViewer::GetDoingPrint(bool* aDoingPrint) {
3172   NS_ENSURE_ARG_POINTER(aDoingPrint);
3173 
3174   // XXX shouldn't this be GetDoingPrint() ?
3175   *aDoingPrint = mPrintJob ? mPrintJob->CreatedForPrintPreview() : false;
3176   return NS_OK;
3177 }
3178 
3179 // XXX This always returns false for subdocuments
3180 NS_IMETHODIMP
GetDoingPrintPreview(bool * aDoingPrintPreview)3181 nsDocumentViewer::GetDoingPrintPreview(bool* aDoingPrintPreview) {
3182   NS_ENSURE_ARG_POINTER(aDoingPrintPreview);
3183 
3184   *aDoingPrintPreview = mPrintJob ? mPrintJob->CreatedForPrintPreview() : false;
3185   return NS_OK;
3186 }
3187 
3188 NS_IMETHODIMP
GetCurrentPrintSettings(nsIPrintSettings ** aCurrentPrintSettings)3189 nsDocumentViewer::GetCurrentPrintSettings(
3190     nsIPrintSettings** aCurrentPrintSettings) {
3191   NS_ENSURE_ARG_POINTER(aCurrentPrintSettings);
3192 
3193   *aCurrentPrintSettings = nullptr;
3194   NS_ENSURE_TRUE(mPrintJob, NS_ERROR_FAILURE);
3195 
3196   *aCurrentPrintSettings = mPrintJob->GetCurrentPrintSettings().take();
3197   return NS_OK;
3198 }
3199 
3200 NS_IMETHODIMP
Cancel()3201 nsDocumentViewer::Cancel() {
3202   NS_ENSURE_TRUE(mPrintJob, NS_ERROR_FAILURE);
3203   return mPrintJob->Cancel();
3204 }
3205 
3206 NS_IMETHODIMP
ExitPrintPreview()3207 nsDocumentViewer::ExitPrintPreview() {
3208   NS_ENSURE_TRUE(mPrintJob, NS_ERROR_FAILURE);
3209 
3210   if (GetIsPrinting()) {
3211     // Block exiting the print preview window if we're in the middle of an
3212     // actual print.
3213     return NS_ERROR_FAILURE;
3214   }
3215 
3216   if (!GetIsPrintPreview()) {
3217     NS_ERROR("Wow, we should never get here!");
3218     return NS_OK;
3219   }
3220 
3221 #  ifdef NS_PRINT_PREVIEW
3222   mPrintJob->Destroy();
3223   mPrintJob = nullptr;
3224 
3225   // Since the print preview implementation discards the window that was used
3226   // to show the print preview, we skip certain cleanup that we would otherwise
3227   // want to do.  Specifically, we do not call `SetIsPrintPreview(false)` to
3228   // unblock navigation, we do not call `SetOverrideDPPX` to reset the
3229   // devicePixelRatio, and we do not call `Show` to make such changes take
3230   // affect.
3231 #  endif  // NS_PRINT_PREVIEW
3232 
3233   return NS_OK;
3234 }
3235 
3236 NS_IMETHODIMP
GetRawNumPages(int32_t * aRawNumPages)3237 nsDocumentViewer::GetRawNumPages(int32_t* aRawNumPages) {
3238   NS_ENSURE_ARG_POINTER(aRawNumPages);
3239   NS_ENSURE_TRUE(mPrintJob, NS_ERROR_FAILURE);
3240 
3241   *aRawNumPages = mPrintJob->GetRawNumPages();
3242   return *aRawNumPages > 0 ? NS_OK : NS_ERROR_FAILURE;
3243 }
3244 
3245 // XXXdholbert As noted in nsIWebBrowserPrint.idl, this API (the IDL attr
3246 // 'printPreviewNumPages') is misnamed and needs s/Page/Sheet/.
3247 // See bug 1669762.
3248 NS_IMETHODIMP
GetPrintPreviewNumPages(int32_t * aPrintPreviewNumPages)3249 nsDocumentViewer::GetPrintPreviewNumPages(int32_t* aPrintPreviewNumPages) {
3250   NS_ENSURE_ARG_POINTER(aPrintPreviewNumPages);
3251   NS_ENSURE_TRUE(mPrintJob, NS_ERROR_FAILURE);
3252   *aPrintPreviewNumPages = mPrintJob->GetPrintPreviewNumSheets();
3253   return *aPrintPreviewNumPages > 0 ? NS_OK : NS_ERROR_FAILURE;
3254 }
3255 
3256 //----------------------------------------------------------------------------------
3257 // Printing/Print Preview Helpers
3258 //----------------------------------------------------------------------------------
3259 
3260 //----------------------------------------------------------------------------------
3261 // Walks the document tree and tells each DocShell whether Printing/PP is
3262 // happening
3263 #endif  // NS_PRINTING
3264 
ShouldAttachToTopLevel()3265 bool nsDocumentViewer::ShouldAttachToTopLevel() {
3266   if (!mParentWidget) {
3267     return false;
3268   }
3269 
3270   if (!mContainer) {
3271     return false;
3272   }
3273 
3274   // We always attach when using puppet widgets
3275   if (nsIWidget::UsePuppetWidgets()) {
3276     return true;
3277   }
3278 
3279 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) || \
3280     defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_UIKIT)
3281   if (!mPresContext) {
3282     return false;
3283   }
3284 
3285   // On windows, in the parent process we also attach, but just to
3286   // chrome items
3287   nsWindowType winType = mParentWidget->WindowType();
3288   if ((winType == eWindowType_toplevel || winType == eWindowType_dialog ||
3289        winType == eWindowType_invisible) &&
3290       mPresContext->IsChrome()) {
3291     return true;
3292   }
3293 #endif
3294 
3295   return false;
3296 }
3297 
3298 //------------------------------------------------------------
3299 // XXX this always returns false for subdocuments
GetIsPrinting() const3300 bool nsDocumentViewer::GetIsPrinting() const {
3301 #ifdef NS_PRINTING
3302   if (mPrintJob) {
3303     return mPrintJob->GetIsPrinting();
3304   }
3305 #endif
3306   return false;
3307 }
3308 
3309 //------------------------------------------------------------
3310 // The PrintJob holds the current value
3311 // this called from inside the DocViewer.
3312 // XXX it always returns false for subdocuments
GetIsPrintPreview() const3313 bool nsDocumentViewer::GetIsPrintPreview() const {
3314 #ifdef NS_PRINTING
3315   return mPrintJob && mPrintJob->CreatedForPrintPreview();
3316 #else
3317   return false;
3318 #endif
3319 }
3320 
3321 //------------------------------------------------------------
3322 // Notification from the PrintJob of the current PP status
SetIsPrintPreview(bool aIsPrintPreview)3323 void nsDocumentViewer::SetIsPrintPreview(bool aIsPrintPreview) {
3324   // Protect against pres shell destruction running scripts.
3325   nsAutoScriptBlocker scriptBlocker;
3326 
3327   if (!aIsPrintPreview) {
3328     InvalidatePotentialSubDocDisplayItem();
3329     if (mPresShell) {
3330       DestroyPresShell();
3331     }
3332     mWindow = nullptr;
3333     mViewManager = nullptr;
3334     mPresContext = nullptr;
3335     mPresShell = nullptr;
3336   }
3337 }
3338 
3339 //----------------------------------------------------------------------------------
3340 // nsIDocumentViewerPrint IFace
3341 //----------------------------------------------------------------------------------
3342 
3343 //------------------------------------------------------------
IncrementDestroyBlockedCount()3344 void nsDocumentViewer::IncrementDestroyBlockedCount() {
3345   ++mDestroyBlockedCount;
3346 }
3347 
DecrementDestroyBlockedCount()3348 void nsDocumentViewer::DecrementDestroyBlockedCount() {
3349   --mDestroyBlockedCount;
3350 }
3351 
3352 //------------------------------------------------------------
3353 // This called ONLY when printing has completed and the DV
3354 // is being notified that it should get rid of the nsPrintJob.
3355 //
3356 // BUT, if we are in Print Preview then we want to ignore the
3357 // notification (we do not get rid of the nsPrintJob)
3358 //
3359 // One small caveat:
3360 //   This IS called from two places in this module for cleaning
3361 //   up when an error occurred during the start up printing
3362 //   and print preview
3363 //
OnDonePrinting()3364 void nsDocumentViewer::OnDonePrinting() {
3365 #if defined(NS_PRINTING) && defined(NS_PRINT_PREVIEW)
3366   // If Destroy() has been called during calling nsPrintJob::Print() or
3367   // nsPrintJob::PrintPreview(), mPrintJob is already nullptr here.
3368   // So, the following clean up does nothing in such case.
3369   // (Do we need some of this for that case?)
3370   if (mPrintJob) {
3371     RefPtr<nsPrintJob> printJob = std::move(mPrintJob);
3372     if (GetIsPrintPreview()) {
3373       printJob->DestroyPrintingData();
3374     } else {
3375       printJob->Destroy();
3376     }
3377 
3378     // We are done printing, now clean up.
3379     //
3380     // For non-print-preview jobs, we are actually responsible for cleaning up
3381     // our whole <browser> or window (see the OPEN_PRINT_BROWSER code), so gotta
3382     // run window.close(), which will take care of this.
3383     //
3384     // For print preview jobs the front-end code is responsible for cleaning the
3385     // UI.
3386     if (!printJob->CreatedForPrintPreview()) {
3387       if (mContainer) {
3388         if (nsCOMPtr<nsPIDOMWindowOuter> win = mContainer->GetWindow()) {
3389           win->Close();
3390         }
3391       }
3392     } else if (mClosingWhilePrinting) {
3393       if (mDocument) {
3394         mDocument->Destroy();
3395         mDocument = nullptr;
3396       }
3397       mClosingWhilePrinting = false;
3398     }
3399   }
3400 #endif  // NS_PRINTING && NS_PRINT_PREVIEW
3401 }
3402 
SetPrintSettingsForSubdocument(nsIPrintSettings * aPrintSettings)3403 NS_IMETHODIMP nsDocumentViewer::SetPrintSettingsForSubdocument(
3404     nsIPrintSettings* aPrintSettings) {
3405 #ifdef NS_PRINTING
3406   {
3407     nsAutoScriptBlocker scriptBlocker;
3408 
3409     if (mPresShell) {
3410       DestroyPresShell();
3411     }
3412 
3413     if (mPresContext) {
3414       DestroyPresContext();
3415     }
3416 
3417     MOZ_ASSERT(!mPresContext);
3418     MOZ_ASSERT(!mPresShell);
3419 
3420     if (MOZ_UNLIKELY(!mDocument)) {
3421       return NS_ERROR_NOT_AVAILABLE;
3422     }
3423 
3424     RefPtr<nsIDeviceContextSpec> devspec = new nsDeviceContextSpecProxy();
3425     nsresult rv =
3426         devspec->Init(nullptr, aPrintSettings, /* aIsPrintPreview = */ true);
3427     NS_ENSURE_SUCCESS(rv, rv);
3428 
3429     mDeviceContext = new nsDeviceContext();
3430     rv = mDeviceContext->InitForPrinting(devspec);
3431 
3432     NS_ENSURE_SUCCESS(rv, rv);
3433 
3434     mPresContext = CreatePresContext(
3435         mDocument, nsPresContext::eContext_PrintPreview, FindContainerView());
3436     mPresContext->SetPrintSettings(aPrintSettings);
3437     rv = mPresContext->Init(mDeviceContext);
3438     NS_ENSURE_SUCCESS(rv, rv);
3439 
3440     rv = MakeWindow(nsSize(mPresContext->DevPixelsToAppUnits(mBounds.width),
3441                            mPresContext->DevPixelsToAppUnits(mBounds.height)),
3442                     FindContainerView());
3443     NS_ENSURE_SUCCESS(rv, rv);
3444 
3445     MOZ_TRY(InitPresentationStuff(true));
3446   }
3447 
3448   RefPtr<PresShell> shell = mPresShell;
3449   shell->FlushPendingNotifications(FlushType::Layout);
3450 #endif
3451   return NS_OK;
3452 }
3453 
SetPageModeForTesting(bool aPageMode,nsIPrintSettings * aPrintSettings)3454 NS_IMETHODIMP nsDocumentViewer::SetPageModeForTesting(
3455     bool aPageMode, nsIPrintSettings* aPrintSettings) {
3456   // XXX Page mode is only partially working; it's currently used for
3457   // reftests that require a paginated context
3458   mIsPageMode = aPageMode;
3459 
3460   // The DestroyPresShell call requires a script blocker, since the
3461   // PresShell::Destroy call it does can cause scripts to run, which could
3462   // re-entrantly call methods on the nsDocumentViewer.
3463   nsAutoScriptBlocker scriptBlocker;
3464 
3465   if (mPresShell) {
3466     DestroyPresShell();
3467   }
3468 
3469   if (mPresContext) {
3470     DestroyPresContext();
3471   }
3472 
3473   mViewManager = nullptr;
3474   mWindow = nullptr;
3475 
3476   NS_ENSURE_STATE(mDocument);
3477   if (aPageMode) {
3478     mPresContext = CreatePresContext(
3479         mDocument, nsPresContext::eContext_PageLayout, FindContainerView());
3480     NS_ENSURE_TRUE(mPresContext, NS_ERROR_OUT_OF_MEMORY);
3481     mPresContext->SetPaginatedScrolling(true);
3482     mPresContext->SetPrintSettings(aPrintSettings);
3483     nsresult rv = mPresContext->Init(mDeviceContext);
3484     NS_ENSURE_SUCCESS(rv, rv);
3485   }
3486   NS_ENSURE_SUCCESS(InitInternal(mParentWidget, nullptr, nullptr, mBounds, true,
3487                                  false, false),
3488                     NS_ERROR_FAILURE);
3489 
3490   Show();
3491   return NS_OK;
3492 }
3493 
3494 NS_IMETHODIMP
GetHistoryEntry(nsISHEntry ** aHistoryEntry)3495 nsDocumentViewer::GetHistoryEntry(nsISHEntry** aHistoryEntry) {
3496   NS_IF_ADDREF(*aHistoryEntry = mSHEntry);
3497   return NS_OK;
3498 }
3499 
3500 NS_IMETHODIMP
GetIsTabModalPromptAllowed(bool * aAllowed)3501 nsDocumentViewer::GetIsTabModalPromptAllowed(bool* aAllowed) {
3502   *aAllowed = !mHidden;
3503   return NS_OK;
3504 }
3505 
3506 NS_IMETHODIMP
GetIsHidden(bool * aHidden)3507 nsDocumentViewer::GetIsHidden(bool* aHidden) {
3508   *aHidden = mHidden;
3509   return NS_OK;
3510 }
3511 
3512 NS_IMETHODIMP
SetIsHidden(bool aHidden)3513 nsDocumentViewer::SetIsHidden(bool aHidden) {
3514   mHidden = aHidden;
3515   return NS_OK;
3516 }
3517 
DestroyPresShell()3518 void nsDocumentViewer::DestroyPresShell() {
3519   // We assert this because destroying the pres shell could otherwise cause
3520   // re-entrancy into nsDocumentViewer methods, and all callers of
3521   // DestroyPresShell need to do other cleanup work afterwards before it
3522   // is safe for those re-entrant method calls to be made.
3523   MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(),
3524              "DestroyPresShell must only be called when scripts are blocked");
3525 
3526   // Break circular reference (or something)
3527   mPresShell->EndObservingDocument();
3528 
3529   RefPtr<mozilla::dom::Selection> selection = GetDocumentSelection();
3530   if (selection && mSelectionListener)
3531     selection->RemoveSelectionListener(mSelectionListener);
3532 
3533   mPresShell->Destroy();
3534   mPresShell = nullptr;
3535 }
3536 
InvalidatePotentialSubDocDisplayItem()3537 void nsDocumentViewer::InvalidatePotentialSubDocDisplayItem() {
3538   if (mViewManager) {
3539     if (nsView* rootView = mViewManager->GetRootView()) {
3540       if (nsView* rootViewParent = rootView->GetParent()) {
3541         if (nsView* subdocview = rootViewParent->GetParent()) {
3542           if (nsIFrame* f = subdocview->GetFrame()) {
3543             if (nsSubDocumentFrame* s = do_QueryFrame(f)) {
3544               s->MarkNeedsDisplayItemRebuild();
3545             }
3546           }
3547         }
3548       }
3549     }
3550   }
3551 }
3552 
DestroyPresContext()3553 void nsDocumentViewer::DestroyPresContext() {
3554   InvalidatePotentialSubDocDisplayItem();
3555   mPresContext = nullptr;
3556 }
3557 
SetPrintPreviewPresentation(nsViewManager * aViewManager,nsPresContext * aPresContext,PresShell * aPresShell)3558 void nsDocumentViewer::SetPrintPreviewPresentation(nsViewManager* aViewManager,
3559                                                    nsPresContext* aPresContext,
3560                                                    PresShell* aPresShell) {
3561   // Protect against pres shell destruction running scripts and re-entrantly
3562   // creating a new presentation.
3563   nsAutoScriptBlocker scriptBlocker;
3564 
3565   if (mPresShell) {
3566     DestroyPresShell();
3567   }
3568 
3569   mWindow = nullptr;
3570   mViewManager = aViewManager;
3571   mPresContext = aPresContext;
3572   mPresShell = aPresShell;
3573 
3574   if (ShouldAttachToTopLevel()) {
3575     DetachFromTopLevelWidget();
3576     nsView* rootView = mViewManager->GetRootView();
3577     rootView->AttachToTopLevelWidget(mParentWidget);
3578     mAttachedToParent = true;
3579   }
3580 }
3581 
3582 // Fires the "document-shown" event so that interested parties are aware of it.
3583 NS_IMETHODIMP
Run()3584 nsDocumentShownDispatcher::Run() {
3585   nsCOMPtr<nsIObserverService> observerService =
3586       mozilla::services::GetObserverService();
3587   if (observerService) {
3588     observerService->NotifyObservers(ToSupports(mDocument), "document-shown",
3589                                      nullptr);
3590   }
3591   return NS_OK;
3592 }
3593