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