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