tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsDocumentViewer.cpp (109940B)


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