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