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/. */
7 /* container for a document and its presentation */
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"
19 #include "nsFrameSelection.h"
21 #include "nsReadableUtils.h"
22 #include "nsIContent.h"
23 #include "nsIDocumentViewer.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 "mozilla/widget/Screen.h"
34 #include "nsPresContext.h"
36 #include "nsIWritablePropertyBag2.h"
37 #include "nsSubDocumentFrame.h"
38 #include "nsGenericHTMLElement.h"
39 #include "nsStubMutationObserver.h"
41 #include "nsISelectionListener.h"
42 #include "mozilla/dom/Selection.h"
43 #include "nsContentUtils.h"
45 # include "mozilla/a11y/DocAccessible.h"
47 #include "mozilla/BasicEvents.h"
48 #include "mozilla/Encoding.h"
49 #include "mozilla/ErrorResult.h"
50 #include "mozilla/Preferences.h"
51 #include "mozilla/SpinEventLoopUntil.h"
52 #include "mozilla/WeakPtr.h"
53 #include "mozilla/StaticPrefs_dom.h"
54 #include "mozilla/StaticPrefs_javascript.h"
55 #include "mozilla/StaticPrefs_fission.h"
56 #include "mozilla/StaticPrefs_print.h"
57 #include "mozilla/StyleSheet.h"
58 #include "mozilla/StyleSheetInlines.h"
59 #include "mozilla/Try.h"
61 #include "nsViewManager.h"
64 #include "nsPageSequenceFrame.h"
65 #include "nsNetUtil.h"
66 #include "nsIDocumentViewerEdit.h"
67 #include "mozilla/css/Loader.h"
68 #include "nsIInterfaceRequestor.h"
69 #include "nsIInterfaceRequestorUtils.h"
70 #include "nsDocShell.h"
71 #include "nsIBaseWindow.h"
72 #include "nsILayoutHistoryState.h"
73 #include "nsCharsetSource.h"
74 #include "mozilla/ReflowInput.h"
75 #include "nsIImageLoadingContent.h"
76 #include "nsCopySupport.h"
77 #include "nsXULPopupManager.h"
79 #include "nsIClipboardHelper.h"
81 #include "nsPIDOMWindow.h"
82 #include "nsGlobalWindowInner.h"
83 #include "nsGlobalWindowOuter.h"
84 #include "nsDOMNavigationTiming.h"
85 #include "nsPIWindowRoot.h"
86 #include "nsJSEnvironment.h"
87 #include "nsFocusManager.h"
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"
99 //--------------------------
101 //---------------------------
104 # include "nsIWebBrowserPrint.h"
106 # include "nsPrintJob.h"
107 # include "nsDeviceContextSpecProxy.h"
110 # include "nsIPrintSettings.h"
111 # include "nsIPrintSettingsService.h"
112 # include "nsISimpleEnumerator.h"
114 #endif // NS_PRINTING
117 #include "nsIDOMEventListener.h"
118 #include "nsISelectionController.h"
120 #include "mozilla/EventDispatcher.h"
121 #include "nsISHEntry.h"
122 #include "nsISHistory.h"
123 #include "nsIWebNavigation.h"
124 #include "mozilla/dom/XMLHttpRequestMainThread.h"
128 #include "mozilla/BasePrincipal.h"
129 #include "mozilla/dom/Element.h"
130 #include "mozilla/dom/Event.h"
131 #include "mozilla/Telemetry.h"
132 #include "mozilla/dom/ScriptLoader.h"
133 #include "mozilla/dom/WindowGlobalChild.h"
137 class PrintPreviewResultInfo
;
139 } // namespace mozilla
141 using namespace mozilla
;
142 using namespace mozilla::dom
;
144 using mozilla::layout::RemotePrintJobChild
;
145 using PrintPreviewResolver
=
146 std::function
<void(const mozilla::dom::PrintPreviewResultInfo
&)>;
148 //-----------------------------------------------------
150 #include "LayoutLogging.h"
151 #include "mozilla/Logging.h"
153 extern mozilla::LazyLogModule gPageCacheLog
;
156 mozilla::LazyLogModule
gPrintingLog("printing");
158 # define PR_PL(_p1) MOZ_LOG(gPrintingLog, mozilla::LogLevel::Debug, _p1);
159 #endif // NS_PRINTING
161 #define PRT_YESNO(_p) ((_p) ? "YES" : "NO")
162 //-----------------------------------------------------
164 class nsDocumentViewer
;
166 // a small delegate class used to avoid circular references
168 class nsDocViewerSelectionListener final
: public nsISelectionListener
{
170 // nsISupports interface...
173 // nsISelectionListerner interface
174 NS_DECL_NSISELECTIONLISTENER
176 explicit nsDocViewerSelectionListener(nsDocumentViewer
* aDocViewer
)
177 : mDocViewer(aDocViewer
), mSelectionWasCollapsed(true) {}
179 void Disconnect() { mDocViewer
= nullptr; }
182 virtual ~nsDocViewerSelectionListener() = default;
184 nsDocumentViewer
* mDocViewer
;
185 bool mSelectionWasCollapsed
;
188 /** editor Implementation of the FocusListener interface */
189 class nsDocViewerFocusListener final
: public nsIDOMEventListener
{
191 explicit nsDocViewerFocusListener(nsDocumentViewer
* aDocViewer
)
192 : mDocViewer(aDocViewer
) {}
195 NS_DECL_NSIDOMEVENTLISTENER
197 void Disconnect() { mDocViewer
= nullptr; }
200 virtual ~nsDocViewerFocusListener() = default;
202 nsDocumentViewer
* mDocViewer
;
205 namespace viewer_detail
{
208 * Mutation observer for use until we hand ourselves over to our SHEntry.
210 class BFCachePreventionObserver final
: public nsStubMutationObserver
{
212 explicit BFCachePreventionObserver(Document
* aDocument
)
213 : mDocument(aDocument
) {}
217 NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
218 NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
219 NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
220 NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
221 NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
222 NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
224 // Stop observing the document.
228 ~BFCachePreventionObserver() = default;
230 // Helper for the work that needs to happen when mutations happen.
231 void MutationHappened();
233 Document
* mDocument
; // Weak; we get notified if it dies
236 NS_IMPL_ISUPPORTS(BFCachePreventionObserver
, nsIMutationObserver
)
238 void BFCachePreventionObserver::CharacterDataChanged(
239 nsIContent
* aContent
, const CharacterDataChangeInfo
&) {
240 if (aContent
->IsInNativeAnonymousSubtree()) {
246 void BFCachePreventionObserver::AttributeChanged(Element
* aElement
,
247 int32_t aNameSpaceID
,
250 const nsAttrValue
* aOldValue
) {
251 if (aElement
->IsInNativeAnonymousSubtree()) {
257 void BFCachePreventionObserver::ContentAppended(nsIContent
* aFirstNewContent
) {
258 if (aFirstNewContent
->IsInNativeAnonymousSubtree()) {
264 void BFCachePreventionObserver::ContentInserted(nsIContent
* aChild
) {
265 if (aChild
->IsInNativeAnonymousSubtree()) {
271 void BFCachePreventionObserver::ContentRemoved(nsIContent
* aChild
,
272 nsIContent
* aPreviousSibling
) {
273 if (aChild
->IsInNativeAnonymousSubtree()) {
279 void BFCachePreventionObserver::NodeWillBeDestroyed(nsINode
* aNode
) {
283 void BFCachePreventionObserver::Disconnect() {
285 mDocument
->RemoveMutationObserver(this);
286 // It will no longer tell us when it goes away, so make sure we're
287 // not holding a dangling ref.
292 void BFCachePreventionObserver::MutationHappened() {
295 "How can we not have a document but be getting notified for mutations?");
296 mDocument
->DisallowBFCaching();
300 } // namespace viewer_detail
302 using viewer_detail::BFCachePreventionObserver
;
304 //-------------------------------------------------------------
305 class nsDocumentViewer final
: public nsIDocumentViewer
,
306 public nsIDocumentViewerEdit
,
307 public nsIDocumentViewerPrint
310 public nsIWebBrowserPrint
314 friend class nsDocViewerSelectionListener
;
315 friend class nsPagePrintTimer
;
316 friend class nsPrintJob
;
321 // nsISupports interface...
324 // nsIDocumentViewer interface...
325 NS_DECL_NSIDOCUMENTVIEWER
327 // nsIDocumentViewerEdit
328 NS_DECL_NSIDOCUMENTVIEWEREDIT
331 // nsIWebBrowserPrint
332 NS_DECL_NSIWEBBROWSERPRINT
335 // nsIDocumentViewerPrint Printing Methods
336 NS_DECL_NSIDOCUMENTVIEWERPRINT
339 virtual ~nsDocumentViewer();
343 * Creates a view manager, root view, and widget for the root view, setting
344 * mViewManager and mWindow.
345 * @param aSize the initial size in appunits
346 * @param aContainerView the container view to hook our root view up
347 * to as a child, or null if this will be the root view manager
349 nsresult
MakeWindow(const nsSize
& aSize
, nsView
* aContainerView
);
352 * Create our device context
354 nsresult
CreateDeviceContext(nsView
* aContainerView
);
357 * If aDoCreation is true, this creates the device context, creates a
358 * prescontext if necessary, and calls MakeWindow.
360 * If aForceSetNewDocument is false, then SetNewDocument won't be
361 * called if the window's current document is already mDocument.
363 nsresult
InitInternal(nsIWidget
* aParentWidget
, nsISupports
* aState
,
364 mozilla::dom::WindowGlobalChild
* aActor
,
365 const nsIntRect
& aBounds
, bool aDoCreation
,
366 bool aNeedMakeCX
= true,
367 bool aForceSetNewDocument
= true);
369 * @param aDoInitialReflow set to true if you want to kick off the initial
372 MOZ_CAN_RUN_SCRIPT_BOUNDARY
373 nsresult
InitPresentationStuff(bool aDoInitialReflow
);
375 already_AddRefed
<nsINode
> GetPopupNode();
376 already_AddRefed
<nsINode
> GetPopupLinkNode();
377 already_AddRefed
<nsIImageLoadingContent
> GetPopupImageNode();
379 void PrepareToStartLoad(void);
381 nsresult
SyncParentSubDocMap();
383 void RemoveFocusListener();
384 void ReinitializeFocusListener();
386 mozilla::dom::Selection
* GetDocumentSelection();
388 void DestroyPresShell();
389 void DestroyPresContext();
391 void InvalidatePotentialSubDocDisplayItem();
393 // Whether we should attach to the top level widget. This is true if we
394 // are sharing/recycling a single base widget and not creating multiple
396 bool ShouldAttachToTopLevel();
398 std::tuple
<const nsIFrame
*, int32_t> GetCurrentSheetFrameAndNumber() const;
401 // Returns the current viewmanager. Might be null.
402 nsViewManager
* GetViewManager();
404 void DetachFromTopLevelWidget();
406 // IMPORTANT: The ownership implicit in the following member
407 // variables has been explicitly checked and set using nsCOMPtr
408 // for owning pointers and raw COM interface pointers for weak
409 // (ie, non owning) references. If you add any members to this
410 // class, please make the ownership explicit (pinkerton, scc).
412 WeakPtr
<nsDocShell
> mContainer
; // it owns me!
413 RefPtr
<nsDeviceContext
> mDeviceContext
; // We create and own this baby
415 // the following six items are explicitly in this order
416 // so they will be destroyed in the reverse order (pinkerton, scc)
417 nsCOMPtr
<Document
> mDocument
;
418 nsCOMPtr
<nsIWidget
> mWindow
; // may be null
419 RefPtr
<nsViewManager
> mViewManager
;
420 RefPtr
<nsPresContext
> mPresContext
;
421 RefPtr
<PresShell
> mPresShell
;
423 RefPtr
<nsDocViewerSelectionListener
> mSelectionListener
;
424 RefPtr
<nsDocViewerFocusListener
> mFocusListener
;
426 nsCOMPtr
<nsIDocumentViewer
> mPreviousViewer
;
427 nsCOMPtr
<nsISHEntry
> mSHEntry
;
428 // Observer that will prevent bfcaching if it gets notified. This
429 // is non-null precisely when mSHEntry is non-null.
430 RefPtr
<BFCachePreventionObserver
> mBFCachePreventionObserver
;
432 nsIWidget
* mParentWidget
; // purposely won't be ref counted. May be null
433 bool mAttachedToParent
; // view is attached to the parent widget
437 int16_t mNumURLStarts
;
438 int16_t mDestroyBlockedCount
;
440 unsigned mStopped
: 1;
441 unsigned mLoaded
: 1;
442 unsigned mDeferredWindowClose
: 1;
443 // document management data
444 // these items are specific to markup documents (html and xml)
445 // may consider splitting these out into a subclass
446 unsigned mIsSticky
: 1;
447 unsigned mInPermitUnload
: 1;
448 unsigned mInPermitUnloadPrompt
: 1;
451 unsigned mClosingWhilePrinting
: 1;
453 # if NS_PRINT_PREVIEW
454 RefPtr
<nsPrintJob
> mPrintJob
;
455 # endif // NS_PRINT_PREVIEW
457 #endif // NS_PRINTING
459 /* character set member data */
460 int32_t mReloadEncodingSource
;
461 const Encoding
* mReloadEncoding
;
464 bool mInitializedForPrintPreview
;
468 class nsDocumentShownDispatcher
: public Runnable
{
470 explicit nsDocumentShownDispatcher(nsCOMPtr
<Document
> aDocument
)
471 : Runnable("nsDocumentShownDispatcher"), mDocument(aDocument
) {}
473 NS_IMETHOD
Run() override
;
476 nsCOMPtr
<Document
> mDocument
;
479 //------------------------------------------------------------------
481 //------------------------------------------------------------------
483 //------------------------------------------------------------------
484 already_AddRefed
<nsIDocumentViewer
> NS_NewDocumentViewer() {
485 RefPtr
<nsDocumentViewer
> viewer
= new nsDocumentViewer();
486 return viewer
.forget();
489 void nsDocumentViewer::PrepareToStartLoad() {
490 MOZ_DIAGNOSTIC_ASSERT(!GetIsPrintPreview(),
491 "Print preview tab should never navigate");
495 mAttachedToParent
= false;
496 mDeferredWindowClose
= false;
499 mClosingWhilePrinting
= false;
501 // Make sure we have destroyed it and cleared the data member
503 mPrintJob
->Destroy();
507 #endif // NS_PRINTING
510 nsDocumentViewer::nsDocumentViewer()
511 : mParentWidget(nullptr),
512 mAttachedToParent(false),
514 mDestroyBlockedCount(0),
517 mDeferredWindowClose(false),
519 mInPermitUnload(false),
520 mInPermitUnloadPrompt(false),
522 mClosingWhilePrinting(false),
523 #endif // NS_PRINTING
524 mReloadEncodingSource(kCharsetUninitialized
),
525 mReloadEncoding(nullptr),
527 mInitializedForPrintPreview(false),
529 PrepareToStartLoad();
532 NS_IMPL_ADDREF(nsDocumentViewer
)
533 NS_IMPL_RELEASE(nsDocumentViewer
)
535 NS_INTERFACE_MAP_BEGIN(nsDocumentViewer
)
536 NS_INTERFACE_MAP_ENTRY(nsIDocumentViewer
)
537 NS_INTERFACE_MAP_ENTRY(nsIDocumentViewerEdit
)
538 NS_INTERFACE_MAP_ENTRY(nsIDocumentViewerPrint
)
539 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIDocumentViewer
)
541 NS_INTERFACE_MAP_ENTRY(nsIWebBrowserPrint
)
545 nsDocumentViewer::~nsDocumentViewer() {
548 mDocument
->Destroy();
553 mPrintJob
->Destroy();
558 MOZ_RELEASE_ASSERT(mDestroyBlockedCount
== 0);
559 NS_ASSERTION(!mPresShell
&& !mPresContext
,
560 "User did not call nsIDocumentViewer::Destroy");
561 if (mPresShell
|| mPresContext
) {
562 // Make sure we don't hand out a reference to the content viewer to
569 if (mSelectionListener
) {
570 mSelectionListener
->Disconnect();
573 RemoveFocusListener();
575 // XXX(?) Revoke pending invalidate events
579 * This method is called by the Document Loader once a document has
580 * been created for a particular data stream... The content viewer
581 * must cache this document for later use when Init(...) is called.
583 * This method is also called when an out of band document.write() happens.
584 * In that case, the document passed in is the same as the previous document.
587 void nsDocumentViewer::LoadStart(Document
* aDocument
) {
588 MOZ_ASSERT(aDocument
);
591 mDocument
= aDocument
;
595 void nsDocumentViewer::RemoveFocusListener() {
596 if (RefPtr
<nsDocViewerFocusListener
> oldListener
=
597 std::move(mFocusListener
)) {
598 oldListener
->Disconnect();
600 mDocument
->RemoveEventListener(u
"focus"_ns
, oldListener
, false);
601 mDocument
->RemoveEventListener(u
"blur"_ns
, oldListener
, false);
606 void nsDocumentViewer::ReinitializeFocusListener() {
607 RemoveFocusListener();
608 mFocusListener
= new nsDocViewerFocusListener(this);
610 mDocument
->AddEventListener(u
"focus"_ns
, mFocusListener
, false, false);
611 mDocument
->AddEventListener(u
"blur"_ns
, mFocusListener
, false, false);
615 nsresult
nsDocumentViewer::SyncParentSubDocMap() {
616 nsCOMPtr
<nsIDocShell
> docShell(mContainer
);
621 nsCOMPtr
<nsPIDOMWindowOuter
> pwin(docShell
->GetWindow());
622 if (!mDocument
|| !pwin
) {
626 nsCOMPtr
<Element
> element
= pwin
->GetFrameElementInternal();
631 nsCOMPtr
<nsIDocShellTreeItem
> parent
;
632 docShell
->GetInProcessParent(getter_AddRefs(parent
));
634 nsCOMPtr
<nsPIDOMWindowOuter
> parent_win
=
635 parent
? parent
->GetWindow() : nullptr;
640 nsCOMPtr
<Document
> parent_doc
= parent_win
->GetDoc();
645 if (mDocument
&& parent_doc
->GetSubDocumentFor(element
) != mDocument
&&
646 parent_doc
->EventHandlingSuppressed()) {
647 mDocument
->SuppressEventHandling(parent_doc
->EventHandlingSuppressed());
649 return parent_doc
->SetSubDocumentFor(element
, mDocument
);
653 nsDocumentViewer::SetContainer(nsIDocShell
* aContainer
) {
654 mContainer
= static_cast<nsDocShell
*>(aContainer
);
656 // We're loading a new document into the window where this document
657 // viewer lives, sync the parent document's frame element -> sub
660 return SyncParentSubDocMap();
664 nsDocumentViewer::GetContainer(nsIDocShell
** aResult
) {
665 NS_ENSURE_ARG_POINTER(aResult
);
667 nsCOMPtr
<nsIDocShell
> container(mContainer
);
668 container
.swap(*aResult
);
673 nsDocumentViewer::Init(nsIWidget
* aParentWidget
, const nsIntRect
& aBounds
,
674 WindowGlobalChild
* aActor
) {
675 return InitInternal(aParentWidget
, nullptr, aActor
, aBounds
, true);
678 nsresult
nsDocumentViewer::InitPresentationStuff(bool aDoInitialReflow
) {
679 // We assert this because initializing the pres shell could otherwise cause
680 // re-entrancy into nsDocumentViewer methods, which might cause a different
681 // pres shell to be created. Callers of InitPresentationStuff should ensure
682 // the call is appropriately bounded by an nsAutoScriptBlocker to decide
683 // when it is safe for these re-entrant calls to be made.
684 MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(),
685 "InitPresentationStuff must only be called when scripts are "
689 // When getting printed, either for print or print preview, the print job
690 // takes care of setting up the presentation of the document.
696 NS_ASSERTION(!mPresShell
, "Someone should have destroyed the presshell!");
698 // Now make the shell for the document
699 nsCOMPtr
<Document
> doc
= mDocument
;
700 RefPtr
<nsPresContext
> presContext
= mPresContext
;
701 RefPtr
<nsViewManager
> viewManager
= mViewManager
;
702 mPresShell
= doc
->CreatePresShell(presContext
, viewManager
);
704 return NS_ERROR_FAILURE
;
707 if (aDoInitialReflow
) {
708 // Since Initialize() will create frames for *all* items
709 // that are currently in the document tree, we need to flush
710 // any pending notifications to prevent the content sink from
711 // duplicating layout frames for content it has added to the tree
712 // but hasn't notified the document about. (Bug 154018)
714 // Note that we are flushing before we add mPresShell as an observer
715 // to avoid bogus notifications.
716 mDocument
->FlushPendingNotifications(FlushType::ContentAndNotify
);
719 mPresShell
->BeginObservingDocument();
721 // Initialize our view manager
724 int32_t p2a
= mPresContext
->AppUnitsPerDevPixel();
727 mPresContext
->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom());
729 nscoord width
= p2a
* mBounds
.width
;
730 nscoord height
= p2a
* mBounds
.height
;
732 mViewManager
->SetWindowDimensions(width
, height
);
733 mPresContext
->SetVisibleArea(nsRect(0, 0, width
, height
));
734 // We rely on the default zoom not being initialized until here.
735 mPresContext
->RecomputeBrowsingContextDependentData();
738 if (mWindow
&& mDocument
->IsTopLevelContentDocument()) {
739 // Set initial safe area insets
740 ScreenIntMargin windowSafeAreaInsets
;
741 LayoutDeviceIntRect windowRect
= mWindow
->GetScreenBounds();
742 nsCOMPtr
<nsIScreen
> screen
= mWindow
->GetWidgetScreen();
744 windowSafeAreaInsets
= nsContentUtils::GetWindowSafeAreaInsets(
745 screen
, mWindow
->GetSafeAreaInsets(), windowRect
);
748 mPresContext
->SetSafeAreaInsets(windowSafeAreaInsets
);
751 if (aDoInitialReflow
) {
752 RefPtr
<PresShell
> presShell
= mPresShell
;
754 presShell
->Initialize();
757 // now register ourselves as a selection listener, so that we get
758 // called when the selection changes in the window
759 if (!mSelectionListener
) {
760 mSelectionListener
= new nsDocViewerSelectionListener(this);
763 RefPtr
<mozilla::dom::Selection
> selection
= GetDocumentSelection();
765 return NS_ERROR_FAILURE
;
768 selection
->AddSelectionListener(mSelectionListener
);
770 ReinitializeFocusListener();
772 if (aDoInitialReflow
&& mDocument
) {
773 nsCOMPtr
<Document
> document
= mDocument
;
774 document
->ScrollToRef();
780 static nsPresContext
* CreatePresContext(Document
* aDocument
,
781 nsPresContext::nsPresContextType aType
,
782 nsView
* aContainerView
) {
783 if (aContainerView
) {
784 return new nsPresContext(aDocument
, aType
);
786 return new nsRootPresContext(aDocument
, aType
);
789 //-----------------------------------------------
790 // This method can be used to initial the "presentation"
791 // The aDoCreation indicates whether it should create
792 // all the new objects or just initialize the existing ones
793 nsresult
nsDocumentViewer::InitInternal(
794 nsIWidget
* aParentWidget
, nsISupports
* aState
, WindowGlobalChild
* aActor
,
795 const nsIntRect
& aBounds
, bool aDoCreation
, bool aNeedMakeCX
/*= true*/,
796 bool aForceSetNewDocument
/* = true*/) {
797 // We don't want any scripts to run here. That can cause flushing,
798 // which can cause reentry into initialization of this document viewer,
799 // which would be disastrous.
800 nsAutoScriptBlocker blockScripts
;
802 mParentWidget
= aParentWidget
; // not ref counted
806 NS_ENSURE_TRUE(mDocument
, NS_ERROR_NULL_POINTER
);
808 nsView
* containerView
= FindContainerView();
812 nsresult rv
= CreateDeviceContext(containerView
);
813 NS_ENSURE_SUCCESS(rv
, rv
);
815 // XXXbz this is a nasty hack to do with the fact that we create
816 // presentations both in Init() and in Show()... Ideally we would only do
817 // it in one place (Show()) and require that callers call init(), open(),
818 // show() in that order or something.
820 (aParentWidget
|| containerView
|| mDocument
->IsBeingUsedAsImage() ||
821 (mDocument
->GetDisplayDocument() &&
822 mDocument
->GetDisplayDocument()->GetPresShell()))) {
823 // Create presentation context
825 // Presentation context already created in SetPageModeForTesting which
826 // is calling this method
828 mPresContext
= CreatePresContext(
829 mDocument
, nsPresContext::eContext_Galley
, containerView
);
831 NS_ENSURE_TRUE(mPresContext
, NS_ERROR_OUT_OF_MEMORY
);
833 nsresult rv
= mPresContext
->Init(mDeviceContext
);
835 mPresContext
= nullptr;
839 #if defined(NS_PRINTING) && defined(NS_PRINT_PREVIEW)
840 makeCX
= !GetIsPrintPreview() &&
841 aNeedMakeCX
; // needs to be true except when we are already in
842 // PP or we are enabling/disabling paginated mode.
849 // Create the ViewManager and Root View...
851 // We must do this before we tell the script global object about
852 // this new document since doing that will cause us to re-enter
853 // into nsSubDocumentFrame code through reflows caused by
854 // FlushPendingNotifications() calls down the road...
856 rv
= MakeWindow(nsSize(mPresContext
->DevPixelsToAppUnits(aBounds
.width
),
857 mPresContext
->DevPixelsToAppUnits(aBounds
.height
)),
859 NS_ENSURE_SUCCESS(rv
, rv
);
862 #ifdef NS_PRINT_PREVIEW
864 // I'm leaving this in a broken state for the moment; we should
865 // be measuring/scaling with the print device context, not the
866 // screen device context, but this is good enough to allow
867 // printing reftests to work.
868 double pageWidth
= 0, pageHeight
= 0;
869 mPresContext
->GetPrintSettings()->GetEffectivePageSize(&pageWidth
,
871 mPresContext
->SetPageSize(
872 nsSize(mPresContext
->CSSTwipsToAppUnits(NSToIntFloor(pageWidth
)),
873 mPresContext
->CSSTwipsToAppUnits(NSToIntFloor(pageHeight
))));
874 mPresContext
->SetIsRootPaginatedDocument(true);
875 mPresContext
->SetPageScale(1.0f
);
879 // Avoid leaking the old viewer.
880 if (mPreviousViewer
) {
881 mPreviousViewer
->Destroy();
882 mPreviousViewer
= nullptr;
887 nsCOMPtr
<nsIInterfaceRequestor
> requestor(mContainer
);
889 // Set script-context-owner in the document
891 nsCOMPtr
<nsPIDOMWindowOuter
> window
= do_GetInterface(requestor
);
894 nsCOMPtr
<Document
> curDoc
= window
->GetExtantDoc();
895 if (aForceSetNewDocument
|| curDoc
!= mDocument
) {
896 rv
= window
->SetNewDocument(mDocument
, aState
, false, aActor
);
905 if (aDoCreation
&& mPresContext
) {
906 // The ViewManager and Root View was created above (in
909 rv
= InitPresentationStuff(!makeCX
);
915 void nsDocumentViewer::SetNavigationTiming(nsDOMNavigationTiming
* timing
) {
916 NS_ASSERTION(mDocument
, "Must have a document to set navigation timing.");
918 mDocument
->SetNavigationTiming(timing
);
923 // LoadComplete(aStatus)
925 // aStatus - The status returned from loading the document.
927 // This method is called by the container when the document has been
928 // completely loaded.
931 nsDocumentViewer::LoadComplete(nsresult aStatus
) {
932 /* We need to protect ourself against auto-destruction in case the
933 window is closed while processing the OnLoad event. See bug
934 http://bugzilla.mozilla.org/show_bug.cgi?id=78445 for more
937 RefPtr
<nsDocumentViewer
> kungFuDeathGrip(this);
939 // Flush out layout so it's up-to-date by the time onload is called.
940 // Note that this could destroy the window, so do this before
941 // checking for our mDocument and its window.
942 if (mPresShell
&& !mStopped
) {
943 // Hold strong ref because this could conceivably run script
944 RefPtr
<PresShell
> presShell
= mPresShell
;
945 presShell
->FlushPendingNotifications(FlushType::Layout
);
949 NS_ENSURE_TRUE(mDocument
, NS_ERROR_NOT_AVAILABLE
);
951 // First, get the window from the document...
952 nsCOMPtr
<nsPIDOMWindowOuter
> window
= mDocument
->GetWindow();
956 // Now, fire either an OnLoad or OnError event to the document...
957 bool restoring
= false;
958 // XXXbz imagelib kills off the document load for a full-page image with
959 // NS_ERROR_PARSED_DATA_CACHED if it's in the cache. So we want to treat
960 // that one as a success code; otherwise whether we fire onload for the image
961 // will depend on whether it's cached!
963 (NS_SUCCEEDED(aStatus
) || aStatus
== NS_ERROR_PARSED_DATA_CACHED
)) {
964 // If this code changes, the code in nsDocLoader::DocLoaderIsEmpty
965 // that fires load events for document.open() cases might need to
967 nsEventStatus status
= nsEventStatus_eIgnore
;
968 WidgetEvent
event(true, eLoad
);
969 event
.mFlags
.mBubbles
= false;
970 event
.mFlags
.mCancelable
= false;
971 // XXX Dispatching to |window|, but using |document| as the target.
972 event
.mTarget
= mDocument
;
974 // If the document presentation is being restored, we don't want to fire
975 // onload to the document content since that would likely confuse scripts
978 RefPtr
<nsDocShell
> docShell
= nsDocShell::Cast(window
->GetDocShell());
979 NS_ENSURE_TRUE(docShell
, NS_ERROR_UNEXPECTED
);
981 // Unfortunately, docShell->GetRestoringDocument() might no longer be set
982 // correctly. In particular, it can be false by now if someone took it upon
983 // themselves to block onload from inside restoration and unblock it later.
984 // But we can detect the restoring case very simply: by whether our
985 // document's readyState is COMPLETE.
987 (mDocument
->GetReadyStateEnum() == Document::READYSTATE_COMPLETE
);
990 mDocument
->GetReadyStateEnum() == Document::READYSTATE_INTERACTIVE
||
991 // test_stricttransportsecurity.html has old-style
992 // docshell-generated about:blank docs reach this code!
993 (mDocument
->GetReadyStateEnum() ==
994 Document::READYSTATE_UNINITIALIZED
&&
995 NS_IsAboutBlank(mDocument
->GetDocumentURI())),
998 bool docShellThinksWeAreRestoring
;
999 docShell
->GetRestoringDocument(&docShellThinksWeAreRestoring
);
1000 MOZ_ASSERT(!docShellThinksWeAreRestoring
,
1001 "How can docshell think we are restoring if we don't have a "
1002 "READYSTATE_COMPLETE document?");
1004 nsCOMPtr
<Document
> d
= mDocument
;
1005 mDocument
->SetReadyStateInternal(Document::READYSTATE_COMPLETE
);
1007 RefPtr
<nsDOMNavigationTiming
> timing(d
->GetNavigationTiming());
1009 timing
->NotifyLoadEventStart();
1012 // Dispatch observer notification to notify observers document load is
1014 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
1016 nsIPrincipal
* principal
= d
->NodePrincipal();
1017 os
->NotifyObservers(ToSupports(d
),
1018 principal
->IsSystemPrincipal()
1019 ? "chrome-document-loaded"
1020 : "content-document-loaded",
1024 nsPIDOMWindowInner
* innerWindow
= window
->GetCurrentInnerWindow();
1025 d
->SetLoadEventFiring(true);
1026 RefPtr
<nsPresContext
> presContext
= mPresContext
;
1027 // MOZ_KnownLive due to bug 1506441
1028 EventDispatcher::Dispatch(
1029 MOZ_KnownLive(nsGlobalWindowOuter::Cast(window
)), presContext
, &event
,
1031 d
->SetLoadEventFiring(false);
1034 timing
->NotifyLoadEventEnd();
1038 innerWindow
->QueuePerformanceNavigationTiming();
1042 // XXX: Should fire error event to the document...
1044 // If our load was explicitly aborted, then we want to set our
1045 // readyState to COMPLETE, and fire a readystatechange event.
1046 if (aStatus
== NS_BINDING_ABORTED
&& mDocument
) {
1047 mDocument
->NotifyAbortedLoad();
1051 // Notify the document that it has been shown (regardless of whether
1052 // it was just loaded). Note: mDocument may be null now if the above
1053 // firing of onload caused the document to unload. Or, mDocument may not be
1054 // the "current active" document, if the above firing of onload caused our
1055 // docshell to navigate away. NOTE: In this latter scenario, it's likely that
1056 // we fired pagehide (when navigating away) without ever having fired
1057 // pageshow, and that's pretty broken... Fortunately, this should be rare.
1058 // (It requires us to spin the event loop in onload handler, e.g. via sync
1059 // XHR, in order for the navigation-away to happen before onload completes.)
1060 // We skip firing pageshow if we're currently handling unload, or if loading
1061 // was explicitly aborted.
1062 if (mDocument
&& mDocument
->IsCurrentActiveDocument() &&
1063 aStatus
!= NS_BINDING_ABORTED
) {
1064 // Re-get window, since it might have changed during above firing of onload
1065 window
= mDocument
->GetWindow();
1067 nsIDocShell
* docShell
= window
->GetDocShell();
1069 if (docShell
&& NS_SUCCEEDED(docShell
->GetIsInUnload(&isInUnload
)) &&
1071 mDocument
->OnPageShow(restoring
, nullptr);
1078 nsCOMPtr
<Document
> document
= mDocument
;
1079 document
->ScrollToRef();
1082 // Now that the document has loaded, we can tell the presshell
1083 // to unsuppress painting.
1085 RefPtr
<PresShell
> presShell
= mPresShell
;
1086 presShell
->UnsuppressPainting();
1087 // mPresShell could have been removed now, see bug 378682/421432
1089 mPresShell
->LoadComplete();
1094 if (mDocument
&& !restoring
) {
1095 mDocument
->LoadEventFired();
1098 // It's probably a good idea to GC soon since we have finished loading.
1099 nsJSContext::PokeGC(
1100 JS::GCReason::LOAD_END
,
1101 mDocument
? mDocument
->GetWrapperPreserveColor() : nullptr);
1104 // Check to see if someone tried to print during the load
1106 auto* outerWin
= nsGlobalWindowOuter::Cast(window
);
1107 outerWin
->StopDelayingPrintingUntilAfterLoad();
1108 if (outerWin
->DelayedPrintUntilAfterLoad()) {
1109 // We call into the inner because it ensures there's an active document
1110 // and such, and it also waits until the whole thing completes, which is
1111 // nice because it allows us to close if needed right here.
1113 nsGlobalWindowInner::Cast(window
->GetCurrentInnerWindow())) {
1114 inner
->Print(IgnoreErrors());
1116 if (outerWin
->DelayedCloseForPrinting()) {
1120 MOZ_ASSERT(!outerWin
->DelayedCloseForPrinting());
1128 bool nsDocumentViewer::GetLoadCompleted() { return mLoaded
; }
1130 bool nsDocumentViewer::GetIsStopped() { return mStopped
; }
1133 nsDocumentViewer::PermitUnload(PermitUnloadAction aAction
,
1134 bool* aPermitUnload
) {
1135 // We're going to be running JS and nested event loops, which could cause our
1136 // DocShell to be destroyed. Make sure we stay alive until the end of the
1138 RefPtr
<nsDocumentViewer
> kungFuDeathGrip(this);
1140 if (StaticPrefs::dom_disable_beforeunload()) {
1141 aAction
= eDontPromptAndUnload
;
1144 *aPermitUnload
= true;
1146 RefPtr
<BrowsingContext
> bc
= mContainer
->GetBrowsingContext();
1151 // Per spec, we need to increase the ignore-opens-during-unload counter while
1152 // dispatching the "beforeunload" event on both the document we're currently
1153 // dispatching the event to and the document that we explicitly asked to
1155 IgnoreOpensDuringUnload
ignoreOpens(mDocument
);
1157 bool foundBlocker
= false;
1158 bool foundOOPListener
= false;
1159 bc
->PreOrderWalk([&](BrowsingContext
* aBC
) {
1160 if (!aBC
->IsInProcess()) {
1161 WindowContext
* wc
= aBC
->GetCurrentWindowContext();
1162 if (wc
&& wc
->HasBeforeUnload()) {
1163 foundOOPListener
= true;
1165 } else if (aBC
->GetDocShell()) {
1166 nsCOMPtr
<nsIDocumentViewer
> viewer(aBC
->GetDocShell()->GetDocViewer());
1167 if (viewer
&& viewer
->DispatchBeforeUnload() == eRequestBlockNavigation
) {
1168 foundBlocker
= true;
1173 if (!foundOOPListener
) {
1174 if (!foundBlocker
) {
1177 if (aAction
!= ePrompt
) {
1178 *aPermitUnload
= aAction
== eDontPromptAndUnload
;
1183 // NB: we nullcheck mDocument because it might now be dead as a result of
1184 // the event being dispatched.
1185 RefPtr
<WindowGlobalChild
> wgc(mDocument
? mDocument
->GetWindowGlobalChild()
1191 nsAutoSyncOperation
sync(mDocument
, SyncOperationBehavior::eSuspendInput
);
1192 AutoSuppressEventHandlingAndSuspend
seh(bc
->Group());
1194 mInPermitUnloadPrompt
= true;
1197 wgc
->SendCheckPermitUnload(
1198 foundBlocker
, aAction
,
1201 *aPermitUnload
= aPermit
;
1204 // If the prompt aborted, we tell our consumer that it is not allowed
1205 // to unload the page. One reason that prompts abort is that the user
1206 // performed some action that caused the page to unload while our prompt
1207 // was active. In those cases we don't want our consumer to also unload
1210 // XXX: Are there other cases where prompts can abort? Is it ok to
1211 // prevent unloading the page in those cases?
1213 *aPermitUnload
= false;
1216 SpinEventLoopUntil("nsDocumentViewer::PermitUnload"_ns
,
1217 [&]() { return done
; });
1219 mInPermitUnloadPrompt
= false;
1223 MOZ_CAN_RUN_SCRIPT_BOUNDARY PermitUnloadResult
1224 nsDocumentViewer::DispatchBeforeUnload() {
1225 AutoDontWarnAboutSyncXHR disableSyncXHRWarning
;
1227 if (!mDocument
|| mInPermitUnload
|| mInPermitUnloadPrompt
) {
1228 return eAllowNavigation
;
1231 // First, get the script global object from the document...
1232 RefPtr
<nsGlobalWindowOuter
> window
=
1233 nsGlobalWindowOuter::Cast(mDocument
->GetWindow());
1235 // This is odd, but not fatal
1236 NS_WARNING("window not set for document!");
1237 return eAllowNavigation
;
1240 NS_ASSERTION(nsContentUtils::IsSafeToRunScript(), "This is unsafe");
1242 // https://html.spec.whatwg.org/multipage/browsing-the-web.html#prompt-to-unload-a-document
1243 // Create an RAII object on mDocument that will increment the
1244 // should-ignore-opens-during-unload counter on initialization
1245 // and decrement it again when it goes out of score (regardless
1246 // of how we exit this function).
1247 IgnoreOpensDuringUnload
ignoreOpens(mDocument
);
1249 // Now, fire an BeforeUnload event to the document and see if it's ok
1251 nsPresContext
* presContext
= mDocument
->GetPresContext();
1252 RefPtr
<BeforeUnloadEvent
> event
=
1253 new BeforeUnloadEvent(mDocument
, presContext
, nullptr);
1254 event
->InitEvent(u
"beforeunload"_ns
, false, true);
1256 // Dispatching to |window|, but using |document| as the target.
1257 event
->SetTarget(mDocument
);
1258 event
->SetTrusted(true);
1260 // In evil cases we might be destroyed while handling the
1261 // onbeforeunload event, don't let that happen. (see also bug#331040)
1262 RefPtr
<nsDocumentViewer
> kungFuDeathGrip(this);
1265 // Never permit popups from the beforeunload handler, no matter
1267 AutoPopupStatePusher
popupStatePusher(PopupBlocker::openAbused
, true);
1269 RefPtr
<BrowsingContext
> bc
= mContainer
->GetBrowsingContext();
1270 NS_ASSERTION(bc
, "should have a browsing context in document viewer");
1272 // Never permit dialogs from the beforeunload handler
1273 nsGlobalWindowOuter::TemporarilyDisableDialogs
disableDialogs(bc
);
1275 Document::PageUnloadingEventTimeStamp
timestamp(mDocument
);
1277 mInPermitUnload
= true;
1278 RefPtr
<nsPresContext
> presContext
= mPresContext
;
1279 EventDispatcher::DispatchDOMEvent(window
, nullptr, event
, presContext
,
1281 mInPermitUnload
= false;
1285 event
->GetReturnValue(text
);
1287 // NB: we nullcheck mDocument because it might now be dead as a result of
1288 // the event being dispatched.
1289 if (window
->AreDialogsEnabled() && mDocument
&&
1290 !(mDocument
->GetSandboxFlags() & SANDBOXED_MODALS
) &&
1291 (!StaticPrefs::dom_require_user_interaction_for_beforeunload() ||
1292 mDocument
->UserHasInteracted()) &&
1293 (event
->WidgetEventPtr()->DefaultPrevented() || !text
.IsEmpty())) {
1294 return eRequestBlockNavigation
;
1296 return eAllowNavigation
;
1300 nsDocumentViewer::GetBeforeUnloadFiring(bool* aInEvent
) {
1301 *aInEvent
= mInPermitUnload
;
1306 nsDocumentViewer::GetInPermitUnload(bool* aInEvent
) {
1307 *aInEvent
= mInPermitUnloadPrompt
;
1312 nsDocumentViewer::PageHide(bool aIsUnload
) {
1313 AutoDontWarnAboutSyncXHR disableSyncXHRWarning
;
1318 return NS_ERROR_NULL_POINTER
;
1322 // Poke the GC. The window might be collectable garbage now.
1323 nsJSContext::PokeGC(JS::GCReason::PAGE_HIDE
,
1324 mDocument
->GetWrapperPreserveColor(),
1325 TimeDuration::FromMilliseconds(
1326 StaticPrefs::javascript_options_gc_delay() * 2));
1329 mDocument
->OnPageHide(!aIsUnload
, nullptr);
1331 // inform the window so that the focus state is reset.
1332 NS_ENSURE_STATE(mDocument
);
1333 nsPIDOMWindowOuter
* window
= mDocument
->GetWindow();
1334 if (window
) window
->PageHidden();
1337 // if Destroy() was called during OnPageHide(), mDocument is nullptr.
1338 NS_ENSURE_STATE(mDocument
);
1340 // First, get the window from the document...
1341 RefPtr
<nsPIDOMWindowOuter
> window
= mDocument
->GetWindow();
1344 // Fail if no window is available...
1345 NS_WARNING("window not set for document!");
1346 return NS_ERROR_NULL_POINTER
;
1349 // https://html.spec.whatwg.org/multipage/browsing-the-web.html#unload-a-document
1350 // Create an RAII object on mDocument that will increment the
1351 // should-ignore-opens-during-unload counter on initialization
1352 // and decrement it again when it goes out of scope.
1353 IgnoreOpensDuringUnload
ignoreOpens(mDocument
);
1355 // Now, fire an Unload event to the document...
1356 nsEventStatus status
= nsEventStatus_eIgnore
;
1357 WidgetEvent
event(true, eUnload
);
1358 event
.mFlags
.mBubbles
= false;
1359 // XXX Dispatching to |window|, but using |document| as the target.
1360 event
.mTarget
= mDocument
;
1362 // Never permit popups from the unload handler, no matter how we get
1364 AutoPopupStatePusher
popupStatePusher(PopupBlocker::openAbused
, true);
1366 Document::PageUnloadingEventTimeStamp
timestamp(mDocument
);
1368 RefPtr
<nsPresContext
> presContext
= mPresContext
;
1369 // MOZ_KnownLive due to bug 1506441
1370 EventDispatcher::Dispatch(MOZ_KnownLive(nsGlobalWindowOuter::Cast(window
)),
1371 presContext
, &event
, nullptr, &status
);
1374 // look for open menupopups and close them after the unload event, in case
1375 // the unload event listeners open any new popups
1376 nsContentUtils::HidePopupsInDocument(mDocument
);
1381 static void AttachContainerRecurse(nsIDocShell
* aShell
) {
1382 nsCOMPtr
<nsIDocumentViewer
> viewer
;
1383 aShell
->GetDocViewer(getter_AddRefs(viewer
));
1385 viewer
->SetIsHidden(false);
1386 Document
* doc
= viewer
->GetDocument();
1388 doc
->SetContainer(static_cast<nsDocShell
*>(aShell
));
1390 if (PresShell
* presShell
= viewer
->GetPresShell()) {
1391 presShell
->SetForwardingContainer(WeakPtr
<nsDocShell
>());
1395 // Now recurse through the children
1397 aShell
->GetInProcessChildCount(&childCount
);
1398 for (int32_t i
= 0; i
< childCount
; ++i
) {
1399 nsCOMPtr
<nsIDocShellTreeItem
> childItem
;
1400 aShell
->GetInProcessChildAt(i
, getter_AddRefs(childItem
));
1401 nsCOMPtr
<nsIDocShell
> shell
= do_QueryInterface(childItem
);
1402 AttachContainerRecurse(shell
);
1407 nsDocumentViewer::Open(nsISupports
* aState
, nsISHEntry
* aSHEntry
) {
1408 NS_ENSURE_TRUE(mPresShell
, NS_ERROR_NOT_INITIALIZED
);
1411 mDocument
->SetContainer(mContainer
);
1414 nsresult rv
= InitInternal(mParentWidget
, aState
, nullptr, mBounds
, false);
1415 NS_ENSURE_SUCCESS(rv
, rv
);
1419 if (mPresShell
) mPresShell
->SetForwardingContainer(WeakPtr
<nsDocShell
>());
1421 // Rehook the child presentations. The child shells are still in
1422 // session history, so get them from there.
1425 nsCOMPtr
<nsIDocShellTreeItem
> item
;
1426 int32_t itemIndex
= 0;
1427 while (NS_SUCCEEDED(
1428 aSHEntry
->ChildShellAt(itemIndex
++, getter_AddRefs(item
))) &&
1430 nsCOMPtr
<nsIDocShell
> shell
= do_QueryInterface(item
);
1431 AttachContainerRecurse(shell
);
1435 SyncParentSubDocMap();
1437 ReinitializeFocusListener();
1439 // XXX re-enable image animations once that works correctly
1441 PrepareToStartLoad();
1443 // When loading a page from the bfcache with puppet widgets, we do the
1444 // widget attachment here (it is otherwise done in MakeWindow, which is
1445 // called for non-bfcache pages in the history, but not bfcache pages).
1446 // Attachment is necessary, since we get detached when another page
1447 // is browsed to. That is, if we are one page A, then when we go to
1448 // page B, we detach. So page A's view has no widget. If we then go
1449 // back to it, and it is in the bfcache, we will use that view, which
1450 // doesn't have a widget. The attach call here will properly attach us.
1451 if (nsIWidget::UsePuppetWidgets() && mPresContext
&&
1452 ShouldAttachToTopLevel()) {
1453 // If the old view is already attached to our parent, detach
1454 DetachFromTopLevelWidget();
1456 nsViewManager
* vm
= GetViewManager();
1457 MOZ_ASSERT(vm
, "no view manager");
1458 nsView
* v
= vm
->GetRootView();
1459 MOZ_ASSERT(v
, "no root view");
1460 MOZ_ASSERT(mParentWidget
, "no mParentWidget to set");
1461 v
->AttachToTopLevelWidget(mParentWidget
);
1463 mAttachedToParent
= true;
1470 nsDocumentViewer::Close(nsISHEntry
* aSHEntry
) {
1471 // All callers are supposed to call close to break circular
1472 // references. If we do this stuff in the destructor, the
1473 // destructor might never be called (especially if we're being
1476 mSHEntry
= aSHEntry
;
1478 // Close is also needed to disable scripts during paint suppression,
1479 // since we transfer the existing global object to the new document
1480 // that is loaded. In the future, the global object may become a proxy
1481 // for an object that can be switched in and out so that we don't need
1482 // to disable scripts during paint suppression.
1484 if (!mDocument
) return NS_OK
;
1487 if (mBFCachePreventionObserver
) {
1488 mBFCachePreventionObserver
->Disconnect();
1490 mBFCachePreventionObserver
= new BFCachePreventionObserver(mDocument
);
1491 mDocument
->AddMutationObserver(mBFCachePreventionObserver
);
1495 // A Close was called while we were printing
1496 // so don't clear the ScriptGlobalObject
1497 // or clear the mDocument below
1498 if (mPrintJob
&& !mClosingWhilePrinting
) {
1499 mClosingWhilePrinting
= true;
1503 // out of band cleanup of docshell
1504 mDocument
->SetScriptGlobalObject(nullptr);
1506 if (!mSHEntry
&& mDocument
) mDocument
->RemovedFromDocShell();
1509 RemoveFocusListener();
1513 static void DetachContainerRecurse(nsIDocShell
* aShell
) {
1514 // Unhook this docshell's presentation
1515 aShell
->SynchronizeLayoutHistoryState();
1516 nsCOMPtr
<nsIDocumentViewer
> viewer
;
1517 aShell
->GetDocViewer(getter_AddRefs(viewer
));
1519 if (Document
* doc
= viewer
->GetDocument()) {
1520 doc
->SetContainer(nullptr);
1522 if (PresShell
* presShell
= viewer
->GetPresShell()) {
1523 auto weakShell
= static_cast<nsDocShell
*>(aShell
);
1524 presShell
->SetForwardingContainer(weakShell
);
1528 // Now recurse through the children
1530 aShell
->GetInProcessChildCount(&childCount
);
1531 for (int32_t i
= 0; i
< childCount
; ++i
) {
1532 nsCOMPtr
<nsIDocShellTreeItem
> childItem
;
1533 aShell
->GetInProcessChildAt(i
, getter_AddRefs(childItem
));
1534 nsCOMPtr
<nsIDocShell
> shell
= do_QueryInterface(childItem
);
1535 DetachContainerRecurse(shell
);
1540 nsDocumentViewer::Destroy() {
1541 // Don't let the document get unloaded while we are printing.
1542 // this could happen if we hit the back button during printing.
1543 // We also keep the viewer from being cached in session history, since
1544 // we require all documents there to be sanitized.
1545 if (mDestroyBlockedCount
!= 0) {
1550 // Here is where we check to see if the document was still being prepared
1551 // for printing when it was asked to be destroy from someone externally
1552 // This usually happens if the document is unloaded while the user is in the
1555 // So we flip the bool to remember that the document is going away
1556 // and we can clean up and abort later after returning from the Print Dialog
1557 if (mPrintJob
&& mPrintJob
->CheckBeforeDestroy()) {
1562 // We want to make sure to disconnect mBFCachePreventionObserver before we
1563 // Sanitize() below.
1564 if (mBFCachePreventionObserver
) {
1565 mBFCachePreventionObserver
->Disconnect();
1566 mBFCachePreventionObserver
= nullptr;
1569 if (mSHEntry
&& mDocument
&& !mDocument
->IsBFCachingAllowed()) {
1570 // Just drop the SHEntry now and pretend like we never even tried to bfcache
1571 // this viewer. This should only happen when someone calls
1572 // DisallowBFCaching() after CanSavePresentation() already ran. Ensure that
1573 // the SHEntry has no viewer and its state is synced up. We want to do this
1574 // via a stack reference, in case those calls mess with our members.
1575 MOZ_LOG(gPageCacheLog
, LogLevel::Debug
,
1576 ("BFCache not allowed, dropping SHEntry"));
1577 nsCOMPtr
<nsISHEntry
> shEntry
= std::move(mSHEntry
);
1578 shEntry
->SetDocumentViewer(nullptr);
1579 shEntry
->SyncPresentationState();
1582 // If we were told to put ourselves into session history instead of destroy
1583 // the presentation, do that now.
1585 if (mPresShell
) mPresShell
->Freeze();
1587 // Make sure the presentation isn't torn down by Hide().
1588 mSHEntry
->SetSticky(mIsSticky
);
1591 // Remove our root view from the view hierarchy.
1593 nsViewManager
* vm
= mPresShell
->GetViewManager();
1595 nsView
* rootView
= vm
->GetRootView();
1598 nsView
* rootViewParent
= rootView
->GetParent();
1599 if (rootViewParent
) {
1600 nsView
* subdocview
= rootViewParent
->GetParent();
1602 nsIFrame
* f
= subdocview
->GetFrame();
1604 nsSubDocumentFrame
* s
= do_QueryFrame(f
);
1606 s
->ClearDisplayItems();
1610 nsViewManager
* parentVM
= rootViewParent
->GetViewManager();
1612 parentVM
->RemoveChild(rootView
);
1621 // This is after Hide() so that the user doesn't see the inputs clear.
1623 mDocument
->Sanitize();
1626 // Reverse ownership. Do this *after* calling sanitize so that sanitize
1627 // doesn't cause mutations that make the SHEntry drop the presentation
1629 // Grab a reference to mSHEntry before calling into things like
1630 // SyncPresentationState that might mess with our members.
1631 nsCOMPtr
<nsISHEntry
> shEntry
=
1632 std::move(mSHEntry
); // we'll need this below
1634 MOZ_LOG(gPageCacheLog
, LogLevel::Debug
,
1635 ("Storing content viewer into cache entry"));
1636 shEntry
->SetDocumentViewer(this);
1638 // Always sync the presentation state. That way even if someone screws up
1639 // and shEntry has no window state at this point we'll be ok; we just won't
1641 shEntry
->SyncPresentationState();
1642 // XXX Synchronize layout history state to parent once bfcache is supported
1643 // in session-history-in-parent.
1645 // Shut down accessibility for the document before we start to tear it down.
1646 #ifdef ACCESSIBILITY
1648 a11y::DocAccessible
* docAcc
= mPresShell
->GetDocAccessible();
1655 // Break the link from the document/presentation to the docshell, so that
1656 // link traversals cannot affect the currently-loaded document.
1657 // When the presentation is restored, Open() and InitInternal() will reset
1658 // these pointers to their original values.
1661 mDocument
->SetContainer(nullptr);
1664 mPresShell
->SetForwardingContainer(mContainer
);
1667 // Do the same for our children. Note that we need to get the child
1668 // docshells from the SHEntry now; the docshell will have cleared them.
1669 nsCOMPtr
<nsIDocShellTreeItem
> item
;
1670 int32_t itemIndex
= 0;
1671 while (NS_SUCCEEDED(
1672 shEntry
->ChildShellAt(itemIndex
++, getter_AddRefs(item
))) &&
1674 nsCOMPtr
<nsIDocShell
> shell
= do_QueryInterface(item
);
1675 DetachContainerRecurse(shell
);
1681 // The document was not put in the bfcache
1683 // Protect against pres shell destruction running scripts and re-entrantly
1684 // creating a new presentation.
1685 nsAutoScriptBlocker scriptBlocker
;
1691 mDocument
->Destroy();
1692 mDocument
= nullptr;
1695 // All callers are supposed to call destroy to break circular
1696 // references. If we do this stuff in the destructor, the
1697 // destructor might never be called (especially if we're being
1702 RefPtr
<nsPrintJob
> printJob
= std::move(mPrintJob
);
1703 # ifdef NS_PRINT_PREVIEW
1704 if (printJob
->CreatedForPrintPreview()) {
1705 printJob
->FinishPrintPreview();
1708 printJob
->Destroy();
1709 MOZ_ASSERT(!mPrintJob
,
1710 "mPrintJob shouldn't be recreated while destroying it");
1714 // Avoid leaking the old viewer.
1715 if (mPreviousViewer
) {
1716 mPreviousViewer
->Destroy();
1717 mPreviousViewer
= nullptr;
1720 mDeviceContext
= nullptr;
1723 DestroyPresContext();
1727 mViewManager
= nullptr;
1728 mContainer
= WeakPtr
<nsDocShell
>();
1734 nsDocumentViewer::Stop(void) {
1735 NS_ASSERTION(mDocument
, "Stop called too early or too late");
1737 mDocument
->StopDocumentLoad();
1742 if (!mLoaded
&& mPresShell
) {
1743 // Well, we might as well paint what we have so far.
1744 RefPtr
<PresShell
> presShell
= mPresShell
; // bug 378682
1745 presShell
->UnsuppressPainting();
1752 nsDocumentViewer::GetDOMDocument(Document
** aResult
) {
1753 NS_ENSURE_TRUE(mDocument
, NS_ERROR_NOT_AVAILABLE
);
1754 nsCOMPtr
<Document
> document
= mDocument
;
1755 document
.forget(aResult
);
1759 Document
* nsDocumentViewer::GetDocument() { return mDocument
; }
1761 nsresult
nsDocumentViewer::SetDocument(Document
* aDocument
) {
1764 // 1) this document viewer has been initialized with a call to Init().
1765 // 2) the stylesheets associated with the document have been added
1768 // XXX Right now, this method assumes that the layout of the current
1769 // document hasn't started yet. More cleanup will probably be
1770 // necessary to make this method work for the case when layout *has*
1771 // occurred for the current document.
1772 // That work can happen when and if it is needed.
1774 if (!aDocument
) return NS_ERROR_NULL_POINTER
;
1776 return SetDocumentInternal(aDocument
, false);
1780 nsDocumentViewer::SetDocumentInternal(Document
* aDocument
,
1781 bool aForceReuseInnerWindow
) {
1782 MOZ_ASSERT(aDocument
);
1784 // Set new container
1785 aDocument
->SetContainer(mContainer
);
1787 if (mDocument
!= aDocument
) {
1788 if (aForceReuseInnerWindow
) {
1789 // Transfer the navigation timing information to the new document, since
1790 // we're keeping the same inner and hence should really have the same
1791 // timing information.
1792 aDocument
->SetNavigationTiming(mDocument
->GetNavigationTiming());
1796 (mDocument
->IsStaticDocument() || aDocument
->IsStaticDocument())) {
1797 nsContentUtils::AddScriptRunner(NewRunnableMethod(
1798 "Document::Destroy", mDocument
, &Document::Destroy
));
1801 // Clear the list of old child docshells. Child docshells for the new
1802 // document will be constructed as frames are created.
1803 if (!aDocument
->IsStaticDocument()) {
1804 nsCOMPtr
<nsIDocShell
> node(mContainer
);
1807 node
->GetInProcessChildCount(&count
);
1808 for (int32_t i
= 0; i
< count
; ++i
) {
1809 nsCOMPtr
<nsIDocShellTreeItem
> child
;
1810 node
->GetInProcessChildAt(0, getter_AddRefs(child
));
1811 node
->RemoveChild(child
);
1816 // Replace the old document with the new one. Do this only when
1817 // the new document really is a new document.
1818 mDocument
= aDocument
;
1820 // Set the script global object on the new document
1821 nsCOMPtr
<nsPIDOMWindowOuter
> window
=
1822 mContainer
? mContainer
->GetWindow() : nullptr;
1825 window
->SetNewDocument(aDocument
, nullptr, aForceReuseInnerWindow
);
1826 if (NS_FAILED(rv
)) {
1833 nsresult rv
= SyncParentSubDocMap();
1834 NS_ENSURE_SUCCESS(rv
, rv
);
1836 // Replace the current pres shell with a new shell for the new document
1838 // Protect against pres shell destruction running scripts and re-entrantly
1839 // creating a new presentation.
1840 nsAutoScriptBlocker scriptBlocker
;
1847 DestroyPresContext();
1850 rv
= InitInternal(mParentWidget
, nullptr, nullptr, mBounds
, true, true,
1857 PresShell
* nsDocumentViewer::GetPresShell() { return mPresShell
; }
1859 nsPresContext
* nsDocumentViewer::GetPresContext() { return mPresContext
; }
1861 nsViewManager
* nsDocumentViewer::GetViewManager() { return mViewManager
; }
1864 nsDocumentViewer::GetBounds(nsIntRect
& aResult
) {
1865 NS_ENSURE_TRUE(mDocument
, NS_ERROR_NOT_AVAILABLE
);
1870 nsIDocumentViewer
* nsDocumentViewer::GetPreviousViewer() {
1871 return mPreviousViewer
;
1874 void nsDocumentViewer::SetPreviousViewer(nsIDocumentViewer
* aViewer
) {
1875 // NOTE: |Show| sets |mPreviousViewer| to null without calling this
1879 NS_ASSERTION(!mPreviousViewer
,
1880 "can't set previous viewer when there already is one");
1882 // In a multiple chaining situation (which occurs when running a thrashing
1883 // test like i-bench or jrgm's tests with no delay), we can build up a
1884 // whole chain of viewers. In order to avoid this, we always set our
1885 // previous viewer to the MOST previous viewer in the chain, and then dump
1886 // the intermediate link from the chain. This ensures that at most only 2
1887 // documents are alive and undestroyed at any given time (the one that is
1888 // showing and the one that is loading with painting suppressed). It's very
1889 // important that if this ever gets changed the code before the
1890 // RestorePresentation call in nsDocShell::InternalLoad be changed
1893 // Make sure we hold a strong ref to prevViewer here, since we'll
1894 // tell aViewer to drop it.
1895 nsCOMPtr
<nsIDocumentViewer
> prevViewer
= aViewer
->GetPreviousViewer();
1897 aViewer
->SetPreviousViewer(nullptr);
1899 return SetPreviousViewer(prevViewer
);
1903 mPreviousViewer
= aViewer
;
1907 nsDocumentViewer::SetBoundsWithFlags(const nsIntRect
& aBounds
,
1909 NS_ENSURE_TRUE(mDocument
, NS_ERROR_NOT_AVAILABLE
);
1911 bool boundsChanged
= !mBounds
.IsEqualEdges(aBounds
);
1914 if (mWindow
&& !mAttachedToParent
) {
1915 // Resize the widget, but don't trigger repaint. Layout will generate
1916 // repaint requests during reflow.
1917 mWindow
->Resize(aBounds
.x
, aBounds
.y
, aBounds
.width
, aBounds
.height
, false);
1918 } else if (mPresContext
&& mViewManager
) {
1919 // Ensure presContext's deviceContext is up to date, as we sometimes get
1920 // here before a resolution-change notification has been fully handled
1921 // during display configuration changes, especially when there are lots
1922 // of windows/widgets competing to handle the notifications.
1923 // (See bug 1154125.)
1924 if (mPresContext
->DeviceContext()->CheckDPIChange()) {
1925 mPresContext
->UIResolutionChangedSync();
1928 int32_t p2a
= mPresContext
->AppUnitsPerDevPixel();
1929 nscoord width
= NSIntPixelsToAppUnits(mBounds
.width
, p2a
);
1930 nscoord height
= NSIntPixelsToAppUnits(mBounds
.height
, p2a
);
1931 nsView
* rootView
= mViewManager
->GetRootView();
1932 if (boundsChanged
&& rootView
) {
1933 nsRect viewDims
= rootView
->GetDimensions();
1934 // If the view/frame tree and prescontext visible area already has the new
1935 // size but we did not, then it's likely that we got reflowed in response
1936 // to a call to GetContentSize. Thus there is a disconnect between the
1937 // size on the document viewer/docshell/containing widget and view
1938 // tree/frame tree/prescontext visible area). SetWindowDimensions compares
1939 // to the root view dimenstions to determine if it needs to do anything;
1940 // if they are the same as the new size it won't do anything, but we still
1941 // need to invalidate because what we want to draw to the screen has
1943 if (viewDims
.width
== width
&& viewDims
.height
== height
) {
1944 nsIFrame
* f
= rootView
->GetFrame();
1946 f
->InvalidateFrame();
1951 mViewManager
->SetWindowDimensions(
1952 width
, height
, !!(aFlags
& nsIDocumentViewer::eDelayResize
));
1955 // If there's a previous viewer, it's the one that's actually showing,
1956 // so be sure to resize it as well so it paints over the right area.
1957 // This may slow down the performance of the new page load, but resize
1958 // during load is also probably a relatively unusual condition
1959 // relating to things being hidden while something is loaded. It so
1960 // happens that Firefox does this a good bit with its infobar, and it
1961 // looks ugly if we don't do this.
1962 if (mPreviousViewer
) {
1963 nsCOMPtr
<nsIDocumentViewer
> previousViewer
= mPreviousViewer
;
1964 previousViewer
->SetBounds(aBounds
);
1971 nsDocumentViewer::SetBounds(const nsIntRect
& aBounds
) {
1972 return SetBoundsWithFlags(aBounds
, 0);
1976 nsDocumentViewer::Move(int32_t aX
, int32_t aY
) {
1977 NS_ENSURE_TRUE(mDocument
, NS_ERROR_NOT_AVAILABLE
);
1978 mBounds
.MoveTo(aX
, aY
);
1980 mWindow
->Move(aX
, aY
);
1986 nsDocumentViewer::Show() {
1987 NS_ENSURE_TRUE(mDocument
, NS_ERROR_NOT_AVAILABLE
);
1989 // We don't need the previous viewer anymore since we're not
1991 if (mPreviousViewer
) {
1992 // This little dance *may* only be to keep
1993 // PresShell::EndObservingDocument happy, but I'm not sure.
1994 nsCOMPtr
<nsIDocumentViewer
> prevViewer(mPreviousViewer
);
1995 mPreviousViewer
= nullptr;
1996 prevViewer
->Destroy();
1998 // Make sure we don't have too many cached ContentViewers
1999 nsCOMPtr
<nsIDocShellTreeItem
> treeItem(mContainer
);
2001 // We need to find the root DocShell since only that object has an
2002 // SHistory and we need the SHistory to evict content viewers
2003 nsCOMPtr
<nsIDocShellTreeItem
> root
;
2004 treeItem
->GetInProcessSameTypeRootTreeItem(getter_AddRefs(root
));
2005 nsCOMPtr
<nsIWebNavigation
> webNav
= do_QueryInterface(root
);
2006 RefPtr
<ChildSHistory
> history
= webNav
->GetSessionHistory();
2007 if (!mozilla::SessionHistoryInParent() && history
) {
2008 int32_t prevIndex
, loadedIndex
;
2009 nsCOMPtr
<nsIDocShell
> docShell
= do_QueryInterface(treeItem
);
2010 docShell
->GetPreviousEntryIndex(&prevIndex
);
2011 docShell
->GetLoadedEntryIndex(&loadedIndex
);
2012 MOZ_LOG(gPageCacheLog
, LogLevel::Verbose
,
2013 ("About to evict content viewers: prev=%d, loaded=%d",
2014 prevIndex
, loadedIndex
));
2015 history
->LegacySHistory()->EvictOutOfRangeDocumentViewers(loadedIndex
);
2021 // When attached to a top level xul window, we do not need to call
2022 // Show on the widget. Underlying window management code handles
2023 // this when the window is initialized.
2024 if (!mAttachedToParent
) {
2025 mWindow
->Show(true);
2029 // Hold on to the document so we can use it after the script blocker below
2030 // has been released (which might re-entrantly call into other
2031 // nsDocumentViewer methods).
2032 nsCOMPtr
<Document
> document
= mDocument
;
2034 if (mDocument
&& !mPresShell
) {
2035 // The InitPresentationStuff call below requires a script blocker, because
2036 // its PresShell::Initialize call can cause scripts to run and therefore
2037 // re-entrant calls to nsDocumentViewer methods to be made.
2038 nsAutoScriptBlocker scriptBlocker
;
2040 NS_ASSERTION(!mWindow
, "Window already created but no presshell?");
2042 nsCOMPtr
<nsIBaseWindow
> base_win(mContainer
);
2044 base_win
->GetParentWidget(&mParentWidget
);
2045 if (mParentWidget
) {
2046 // GetParentWidget AddRefs, but mParentWidget is weak
2047 mParentWidget
->Release();
2051 nsView
* containerView
= FindContainerView();
2053 nsresult rv
= CreateDeviceContext(containerView
);
2054 NS_ENSURE_SUCCESS(rv
, rv
);
2056 // Create presentation context
2057 NS_ASSERTION(!mPresContext
,
2058 "Shouldn't have a prescontext if we have no shell!");
2059 mPresContext
= CreatePresContext(mDocument
, nsPresContext::eContext_Galley
,
2061 NS_ENSURE_TRUE(mPresContext
, NS_ERROR_OUT_OF_MEMORY
);
2063 rv
= mPresContext
->Init(mDeviceContext
);
2064 if (NS_FAILED(rv
)) {
2065 mPresContext
= nullptr;
2069 rv
= MakeWindow(nsSize(mPresContext
->DevPixelsToAppUnits(mBounds
.width
),
2070 mPresContext
->DevPixelsToAppUnits(mBounds
.height
)),
2072 if (NS_FAILED(rv
)) return rv
;
2077 rv
= InitPresentationStuff(mDocument
->MayStartLayout());
2080 // If we get here the document load has already started and the
2081 // window is shown because some JS on the page caused it to be
2085 RefPtr
<PresShell
> presShell
= mPresShell
; // bug 378682
2086 presShell
->UnsuppressPainting();
2090 // Notify observers that a new page has been shown. This will get run
2091 // from the event loop after we actually draw the page.
2092 RefPtr
<nsDocumentShownDispatcher
> event
=
2093 new nsDocumentShownDispatcher(document
);
2094 document
->Dispatch(event
.forget());
2100 nsDocumentViewer::Hide() {
2101 if (!mAttachedToParent
&& mWindow
) {
2102 mWindow
->Show(false);
2105 if (!mPresShell
) return NS_OK
;
2107 NS_ASSERTION(mPresContext
, "Can't have a presshell and no prescontext!");
2109 // Avoid leaking the old viewer.
2110 if (mPreviousViewer
) {
2111 mPreviousViewer
->Destroy();
2112 mPreviousViewer
= nullptr;
2116 // This window is sticky, that means that it might be shown again
2117 // and we don't want the presshell n' all that to be thrown away
2118 // just because the window is hidden.
2123 nsCOMPtr
<nsIDocShell
> docShell(mContainer
);
2126 nsCOMPtr
<nsIDocumentViewer
> currentViewer
;
2127 docShell
->GetDocViewer(getter_AddRefs(currentViewer
));
2128 MOZ_ASSERT(currentViewer
== this);
2130 nsCOMPtr
<nsILayoutHistoryState
> layoutState
;
2131 mPresShell
->CaptureHistoryState(getter_AddRefs(layoutState
));
2134 // Do not run ScriptRunners queued by DestroyPresShell() in the intermediate
2135 // state before we're done destroying PresShell, PresContext, ViewManager,
2137 nsAutoScriptBlocker scriptBlocker
;
2141 DestroyPresContext();
2143 mViewManager
= nullptr;
2145 mDeviceContext
= nullptr;
2146 mParentWidget
= nullptr;
2148 nsCOMPtr
<nsIBaseWindow
> base_win(mContainer
);
2150 if (base_win
&& !mAttachedToParent
) {
2151 base_win
->SetParentWidget(nullptr);
2158 nsDocumentViewer::GetSticky(bool* aSticky
) {
2159 *aSticky
= mIsSticky
;
2165 nsDocumentViewer::SetSticky(bool aSticky
) {
2166 mIsSticky
= aSticky
;
2172 nsDocumentViewer::ClearHistoryEntry() {
2174 nsJSContext::PokeGC(JS::GCReason::PAGE_HIDE
,
2175 mDocument
->GetWrapperPreserveColor(),
2176 TimeDuration::FromMilliseconds(
2177 StaticPrefs::javascript_options_gc_delay() * 2));
2184 //-------------------------------------------------------
2186 nsresult
nsDocumentViewer::MakeWindow(const nsSize
& aSize
,
2187 nsView
* aContainerView
) {
2188 if (GetIsPrintPreview()) {
2192 bool shouldAttach
= ShouldAttachToTopLevel();
2195 // If the old view is already attached to our parent, detach
2196 DetachFromTopLevelWidget();
2199 mViewManager
= new nsViewManager();
2201 nsDeviceContext
* dx
= mPresContext
->DeviceContext();
2203 nsresult rv
= mViewManager
->Init(dx
);
2204 if (NS_FAILED(rv
)) return rv
;
2206 // The root view is always at 0,0.
2207 nsRect
tbounds(nsPoint(0, 0), aSize
);
2209 nsView
* view
= mViewManager
->CreateView(tbounds
, aContainerView
);
2210 if (!view
) return NS_ERROR_OUT_OF_MEMORY
;
2212 // Create a widget if we were given a parent widget or don't have a
2213 // container view that we can hook up to without a widget.
2214 // Don't create widgets for ResourceDocs (external resources & svg images),
2215 // because when they're displayed, they're painted into *another* document's
2217 if (!mDocument
->IsResourceDoc() && (mParentWidget
|| !aContainerView
)) {
2218 // pass in a native widget to be the parent widget ONLY if the view
2219 // hierarchy will stand alone. otherwise the view will find its own parent
2220 // widget and "do the right thing" to establish a parent/child widget
2222 widget::InitData initData
;
2223 widget::InitData
* initDataPtr
;
2224 if (!mParentWidget
) {
2225 initDataPtr
= &initData
;
2226 initData
.mWindowType
= widget::WindowType::Invisible
;
2228 initDataPtr
= nullptr;
2232 // Reuse the top level parent widget.
2233 rv
= view
->AttachToTopLevelWidget(mParentWidget
);
2234 mAttachedToParent
= true;
2235 } else if (!aContainerView
&& mParentWidget
) {
2236 rv
= view
->CreateWidgetForParent(mParentWidget
, initDataPtr
, true, false);
2238 rv
= view
->CreateWidget(initDataPtr
, true, false);
2240 if (NS_FAILED(rv
)) return rv
;
2243 // Setup hierarchical relationship in view manager
2244 mViewManager
->SetRootView(view
);
2246 mWindow
= view
->GetWidget();
2248 // This SetFocus is necessary so the Arrow Key and Page Key events
2249 // go to the scrolled view as soon as the Window is created instead of going
2250 // to the browser window (this enables keyboard scrolling of the document)
2251 // mWindow->SetFocus();
2256 void nsDocumentViewer::DetachFromTopLevelWidget() {
2258 nsView
* oldView
= mViewManager
->GetRootView();
2259 if (oldView
&& oldView
->IsAttachedToTopLevel()) {
2260 oldView
->DetachFromTopLevelWidget();
2263 mAttachedToParent
= false;
2266 nsView
* nsDocumentViewer::FindContainerView() {
2271 nsCOMPtr
<nsIDocShell
> docShell(mContainer
);
2272 nsCOMPtr
<nsPIDOMWindowOuter
> pwin(docShell
->GetWindow());
2277 nsCOMPtr
<Element
> containerElement
= pwin
->GetFrameElementInternal();
2278 if (!containerElement
) {
2282 nsIFrame
* subdocFrame
= containerElement
->GetPrimaryFrame();
2284 // XXX Silenced by default in bug 1175289
2285 LAYOUT_WARNING("Subdocument container has no frame");
2289 // Check subdocFrame just to be safe. If this somehow fails we treat that as
2290 // display:none, the document is not displayed.
2291 if (!subdocFrame
->IsSubDocumentFrame()) {
2292 NS_WARNING_ASSERTION(subdocFrame
->Type() == LayoutFrameType::None
,
2293 "Subdocument container has non-subdocument frame");
2297 NS_ASSERTION(subdocFrame
->GetView(), "Subdoc frames must have views");
2298 return static_cast<nsSubDocumentFrame
*>(subdocFrame
)->EnsureInnerView();
2301 nsresult
nsDocumentViewer::CreateDeviceContext(nsView
* aContainerView
) {
2302 MOZ_ASSERT(!mPresShell
&& !mWindow
,
2303 "This will screw up our existing presentation");
2304 MOZ_ASSERT(mDocument
, "Gotta have a document here");
2306 Document
* doc
= mDocument
->GetDisplayDocument();
2308 NS_ASSERTION(!aContainerView
,
2309 "External resource document embedded somewhere?");
2310 // We want to use our display document's device context if possible
2311 nsPresContext
* ctx
= doc
->GetPresContext();
2313 mDeviceContext
= ctx
->DeviceContext();
2318 // Create a device context even if we already have one, since our widget
2319 // might have changed.
2320 nsIWidget
* widget
= nullptr;
2321 if (aContainerView
) {
2322 widget
= aContainerView
->GetNearestWidget(nullptr);
2325 widget
= mParentWidget
;
2328 widget
= widget
->GetTopLevelWidget();
2331 mDeviceContext
= new nsDeviceContext();
2332 mDeviceContext
->Init(widget
);
2336 // Return the selection for the document. Note that text fields have their
2337 // own selection, which cannot be accessed with this method.
2338 mozilla::dom::Selection
* nsDocumentViewer::GetDocumentSelection() {
2343 return mPresShell
->GetCurrentSelection(SelectionType::eNormal
);
2346 /* ============================================================================
2347 * nsIDocumentViewerEdit
2348 * ============================================================================
2351 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP
nsDocumentViewer::ClearSelection() {
2352 // use nsCopySupport::GetSelectionForCopy() ?
2353 RefPtr
<mozilla::dom::Selection
> selection
= GetDocumentSelection();
2355 return NS_ERROR_FAILURE
;
2359 selection
->CollapseToStart(rv
);
2360 return rv
.StealNSResult();
2363 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP
nsDocumentViewer::SelectAll() {
2364 // XXX this is a temporary implementation copied from nsWebShell
2365 // for now. I think Document and friends should have some helper
2366 // functions to make this easier.
2368 // use nsCopySupport::GetSelectionForCopy() ?
2369 RefPtr
<mozilla::dom::Selection
> selection
= GetDocumentSelection();
2371 return NS_ERROR_FAILURE
;
2375 return NS_ERROR_FAILURE
;
2378 nsCOMPtr
<nsINode
> bodyNode
;
2379 if (mDocument
->IsHTMLOrXHTML()) {
2380 // XXXbz why not just do GetBody() for all documents, then GetRootElement()
2381 // if GetBody() is null?
2382 bodyNode
= mDocument
->GetBody();
2384 bodyNode
= mDocument
->GetRootElement();
2386 if (!bodyNode
) return NS_ERROR_FAILURE
;
2389 selection
->RemoveAllRanges(err
);
2391 return err
.StealNSResult();
2394 mozilla::dom::Selection::AutoUserInitiated
userSelection(selection
);
2395 selection
->SelectAllChildren(*bodyNode
, err
);
2396 return err
.StealNSResult();
2399 NS_IMETHODIMP
nsDocumentViewer::CopySelection() {
2400 RefPtr
<PresShell
> presShell
= mPresShell
;
2401 nsCopySupport::FireClipboardEvent(eCopy
, nsIClipboard::kGlobalClipboard
,
2402 presShell
, nullptr);
2406 NS_IMETHODIMP
nsDocumentViewer::CopyLinkLocation() {
2407 NS_ENSURE_TRUE(mPresShell
, NS_ERROR_NOT_INITIALIZED
);
2408 nsCOMPtr
<nsINode
> node
= GetPopupLinkNode();
2409 // make noise if we're not in a link
2410 NS_ENSURE_TRUE(node
, NS_ERROR_FAILURE
);
2412 nsCOMPtr
<dom::Element
> elm(do_QueryInterface(node
));
2413 NS_ENSURE_TRUE(elm
, NS_ERROR_FAILURE
);
2415 nsAutoString locationText
;
2416 nsContentUtils::GetLinkLocation(elm
, locationText
);
2417 if (locationText
.IsEmpty()) return NS_ERROR_FAILURE
;
2419 nsresult rv
= NS_OK
;
2420 nsCOMPtr
<nsIClipboardHelper
> clipboard(
2421 do_GetService("@mozilla.org/widget/clipboardhelper;1", &rv
));
2422 NS_ENSURE_SUCCESS(rv
, rv
);
2424 // copy the href onto the clipboard
2425 return clipboard
->CopyString(locationText
);
2428 NS_IMETHODIMP
nsDocumentViewer::CopyImage(int32_t aCopyFlags
) {
2429 NS_ENSURE_TRUE(mPresShell
, NS_ERROR_NOT_INITIALIZED
);
2430 nsCOMPtr
<nsIImageLoadingContent
> node
= GetPopupImageNode();
2431 // make noise if we're not in an image
2432 NS_ENSURE_TRUE(node
, NS_ERROR_FAILURE
);
2434 nsCOMPtr
<nsILoadContext
> loadContext(mContainer
);
2435 return nsCopySupport::ImageCopy(node
, loadContext
, aCopyFlags
);
2438 NS_IMETHODIMP
nsDocumentViewer::GetCopyable(bool* aCopyable
) {
2439 NS_ENSURE_ARG_POINTER(aCopyable
);
2440 *aCopyable
= nsCopySupport::CanCopy(mDocument
);
2444 NS_IMETHODIMP
nsDocumentViewer::GetContents(const char* mimeType
,
2446 nsAString
& aOutValue
) {
2447 aOutValue
.Truncate();
2449 NS_ENSURE_TRUE(mPresShell
, NS_ERROR_NOT_INITIALIZED
);
2450 NS_ENSURE_TRUE(mDocument
, NS_ERROR_NOT_INITIALIZED
);
2452 // Now we have the selection. Make sure it's nonzero:
2453 RefPtr
<Selection
> sel
;
2454 if (selectionOnly
) {
2455 sel
= nsCopySupport::GetSelectionForCopy(mDocument
);
2456 NS_ENSURE_TRUE(sel
, NS_ERROR_FAILURE
);
2458 if (NS_WARN_IF(sel
->IsCollapsed())) {
2463 // call the copy code
2464 return nsCopySupport::GetContents(nsDependentCString(mimeType
), 0, sel
,
2465 mDocument
, aOutValue
);
2468 NS_IMETHODIMP
nsDocumentViewer::GetCanGetContents(bool* aCanGetContents
) {
2469 NS_ENSURE_ARG_POINTER(aCanGetContents
);
2470 *aCanGetContents
= false;
2471 NS_ENSURE_STATE(mDocument
);
2472 *aCanGetContents
= nsCopySupport::CanCopy(mDocument
);
2476 NS_IMETHODIMP
nsDocumentViewer::SetCommandNode(nsINode
* aNode
) {
2477 Document
* document
= GetDocument();
2478 NS_ENSURE_STATE(document
);
2480 nsCOMPtr
<nsPIDOMWindowOuter
> window(document
->GetWindow());
2481 NS_ENSURE_TRUE(window
, NS_ERROR_NOT_AVAILABLE
);
2483 nsCOMPtr
<nsPIWindowRoot
> root
= window
->GetTopWindowRoot();
2484 NS_ENSURE_STATE(root
);
2486 root
->SetPopupNode(aNode
);
2491 nsDocumentViewer::GetDeviceFullZoomForTest(float* aDeviceFullZoom
) {
2492 NS_ENSURE_ARG_POINTER(aDeviceFullZoom
);
2493 nsPresContext
* pc
= GetPresContext();
2494 *aDeviceFullZoom
= pc
? pc
->GetDeviceFullZoom() : 1.0;
2499 nsDocumentViewer::SetAuthorStyleDisabled(bool aStyleDisabled
) {
2501 mPresShell
->SetAuthorStyleDisabled(aStyleDisabled
);
2507 nsDocumentViewer::GetAuthorStyleDisabled(bool* aStyleDisabled
) {
2509 *aStyleDisabled
= mPresShell
->GetAuthorStyleDisabled();
2511 *aStyleDisabled
= false;
2516 /* [noscript,notxpcom] Encoding getHintCharset (); */
2517 NS_IMETHODIMP_(const Encoding
*)
2518 nsDocumentViewer::GetReloadEncodingAndSource(int32_t* aSource
) {
2519 *aSource
= mReloadEncodingSource
;
2520 if (kCharsetUninitialized
== mReloadEncodingSource
) {
2523 return mReloadEncoding
;
2526 NS_IMETHODIMP_(void)
2527 nsDocumentViewer::SetReloadEncodingAndSource(const Encoding
* aEncoding
,
2530 aSource
== kCharsetUninitialized
||
2532 kCharsetFromFinalAutoDetectionWouldHaveBeenUTF8InitialWasASCII
&&
2534 kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLDInitialWasASCII
) ||
2535 aSource
== kCharsetFromFinalUserForcedAutoDetection
);
2536 mReloadEncoding
= aEncoding
;
2537 mReloadEncodingSource
= aSource
;
2540 NS_IMETHODIMP_(void)
2541 nsDocumentViewer::ForgetReloadEncoding() {
2542 mReloadEncoding
= nullptr;
2543 mReloadEncodingSource
= kCharsetUninitialized
;
2546 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP
nsDocumentViewer::GetContentSize(
2547 int32_t aMaxWidth
, int32_t aMaxHeight
, int32_t aPrefWidth
, int32_t* aWidth
,
2549 RefPtr
<BrowsingContext
> bc
= mContainer
->GetBrowsingContext();
2550 NS_ENSURE_TRUE(bc
, NS_ERROR_NOT_AVAILABLE
);
2552 // It's only valid to access this from a top frame. Doesn't work from
2554 NS_ENSURE_TRUE(bc
->IsTop(), NS_ERROR_FAILURE
);
2556 // Convert max-width/height and pref-width to app units.
2557 if (aMaxWidth
> 0) {
2558 aMaxWidth
= CSSPixel::ToAppUnits(aMaxWidth
);
2560 aMaxWidth
= NS_UNCONSTRAINEDSIZE
;
2562 if (aMaxHeight
> 0) {
2563 aMaxHeight
= CSSPixel::ToAppUnits(aMaxHeight
);
2565 aMaxHeight
= NS_UNCONSTRAINEDSIZE
;
2567 if (aPrefWidth
> 0) {
2568 aPrefWidth
= CSSPixel::ToAppUnits(aPrefWidth
);
2573 RefPtr
<PresShell
> presShell
= GetPresShell();
2574 NS_ENSURE_TRUE(presShell
, NS_ERROR_FAILURE
);
2576 // Flush out all content and style updates. We can't use a resize reflow
2577 // because it won't change some sizes that a style change reflow will.
2578 mDocument
->FlushPendingNotifications(FlushType::Layout
);
2580 nsIFrame
* root
= presShell
->GetRootFrame();
2581 NS_ENSURE_TRUE(root
, NS_ERROR_FAILURE
);
2583 WritingMode wm
= root
->GetWritingMode();
2587 const auto& constraints
= presShell
->GetWindowSizeConstraints();
2588 aMaxHeight
= std::min(aMaxHeight
, constraints
.mMaxSize
.height
);
2589 aMaxWidth
= std::min(aMaxWidth
, constraints
.mMaxSize
.width
);
2591 UniquePtr
<gfxContext
> rcx(presShell
->CreateReferenceRenderingContext());
2592 const nscoord minISize
= wm
.IsVertical() ? constraints
.mMinSize
.height
2593 : constraints
.mMinSize
.width
;
2594 const nscoord maxISize
= wm
.IsVertical() ? aMaxHeight
: aMaxWidth
;
2596 prefISize
= std::max(root
->GetMinISize(rcx
.get()), aPrefWidth
);
2598 prefISize
= root
->GetPrefISize(rcx
.get());
2600 prefISize
= nsPresContext::RoundUpAppUnitsToCSSPixel(
2601 std::max(minISize
, std::min(prefISize
, maxISize
)));
2604 // We should never intentionally get here with this sentinel value, but it's
2605 // possible that a document with huge sizes might inadvertently have a
2606 // prefISize that exactly matches NS_UNCONSTRAINEDSIZE.
2607 // Just bail if that happens.
2608 NS_ENSURE_TRUE(prefISize
!= NS_UNCONSTRAINEDSIZE
, NS_ERROR_FAILURE
);
2610 nscoord height
= wm
.IsVertical() ? prefISize
: aMaxHeight
;
2611 nscoord width
= wm
.IsVertical() ? aMaxWidth
: prefISize
;
2613 presShell
->ResizeReflow(width
, height
, ResizeReflowOptions::BSizeLimit
);
2615 RefPtr
<nsPresContext
> presContext
= GetPresContext();
2616 NS_ENSURE_TRUE(presContext
, NS_ERROR_FAILURE
);
2618 // Protect against bogus returns here
2619 nsRect shellArea
= presContext
->GetVisibleArea();
2620 NS_ENSURE_TRUE(shellArea
.width
!= NS_UNCONSTRAINEDSIZE
&&
2621 shellArea
.height
!= NS_UNCONSTRAINEDSIZE
,
2624 // Ceil instead of rounding here, so we can actually guarantee showing all the
2626 *aWidth
= std::ceil(CSSPixel::FromAppUnits(shellArea
.width
));
2627 *aHeight
= std::ceil(CSSPixel::FromAppUnits(shellArea
.height
));
2632 NS_IMPL_ISUPPORTS(nsDocViewerSelectionListener
, nsISelectionListener
)
2635 * GetPopupNode, GetPopupLinkNode and GetPopupImageNode are helpers
2636 * for the cmd_copyLink / cmd_copyImageLocation / cmd_copyImageContents family
2637 * of commands. The focus controller stores the popup node, these retrieve
2638 * them and munge appropriately. Note that we have to store the popup node
2639 * rather than retrieving it from EventStateManager::GetFocusedContent because
2640 * not all content (images included) can receive focus.
2643 already_AddRefed
<nsINode
> nsDocumentViewer::GetPopupNode() {
2645 Document
* document
= GetDocument();
2646 NS_ENSURE_TRUE(document
, nullptr);
2648 // get the private dom window
2649 nsCOMPtr
<nsPIDOMWindowOuter
> window(document
->GetWindow());
2650 NS_ENSURE_TRUE(window
, nullptr);
2652 nsCOMPtr
<nsPIWindowRoot
> root
= window
->GetTopWindowRoot();
2653 NS_ENSURE_TRUE(root
, nullptr);
2655 // get the popup node
2656 nsCOMPtr
<nsINode
> node
= root
->GetPopupNode();
2658 nsPIDOMWindowOuter
* rootWindow
= root
->GetWindow();
2660 nsCOMPtr
<Document
> rootDoc
= rootWindow
->GetExtantDoc();
2662 nsXULPopupManager
* pm
= nsXULPopupManager::GetInstance();
2664 node
= pm
->GetLastTriggerPopupNode(rootDoc
);
2669 return node
.forget();
2675 // GetPopupLinkNode: return popup link node or fail
2676 already_AddRefed
<nsINode
> nsDocumentViewer::GetPopupLinkNode() {
2678 nsCOMPtr
<nsINode
> node
= GetPopupNode();
2680 // find out if we have a link in our ancestry
2682 if (const auto* element
= Element::FromNode(*node
)) {
2683 if (element
->IsLink()) {
2684 return node
.forget();
2688 // get our parent and keep trying...
2689 node
= node
->GetParentNode();
2692 // if we have no node, fail
2696 // GetPopupLinkNode: return popup image node or fail
2697 already_AddRefed
<nsIImageLoadingContent
> nsDocumentViewer::GetPopupImageNode() {
2699 nsCOMPtr
<nsINode
> node
= GetPopupNode();
2700 nsCOMPtr
<nsIImageLoadingContent
> img
= do_QueryInterface(node
);
2701 return img
.forget();
2707 * These two functions -- GetInLink and GetInImage -- are kind of annoying
2708 * in that they only get called from the controller (in
2709 * nsDOMWindowController::IsCommandEnabled). The actual construction of the
2710 * context menus in communicator (nsContextMenu.js) has its own, redundant
2711 * tests. No big deal, but good to keep in mind if we ever clean context
2715 NS_IMETHODIMP
nsDocumentViewer::GetInLink(bool* aInLink
) {
2717 printf("dr :: nsDocumentViewer::GetInLink\n");
2720 NS_ENSURE_ARG_POINTER(aInLink
);
2722 // we're not in a link unless i say so
2725 // get the popup link
2726 nsCOMPtr
<nsINode
> node
= GetPopupLinkNode();
2728 return NS_ERROR_FAILURE
;
2731 // if we made it here, we're in a link
2736 NS_IMETHODIMP
nsDocumentViewer::GetInImage(bool* aInImage
) {
2738 printf("dr :: nsDocumentViewer::GetInImage\n");
2741 NS_ENSURE_ARG_POINTER(aInImage
);
2743 // we're not in an image unless i say so
2746 // get the popup image
2747 nsCOMPtr
<nsIImageLoadingContent
> node
= GetPopupImageNode();
2749 return NS_ERROR_FAILURE
;
2752 // Make sure there is a URI assigned. This allows <input type="image"> to
2753 // be an image but rejects other <input> types. This matches what
2754 // nsContextMenu.js does.
2755 nsCOMPtr
<nsIURI
> uri
;
2756 node
->GetCurrentURI(getter_AddRefs(uri
));
2758 // if we made it here, we're in an image
2765 NS_IMETHODIMP
nsDocViewerSelectionListener::NotifySelectionChanged(
2766 Document
*, Selection
*, int16_t aReason
, int32_t aAmount
) {
2771 // get the selection state
2772 RefPtr
<mozilla::dom::Selection
> selection
=
2773 mDocViewer
->GetDocumentSelection();
2775 return NS_ERROR_FAILURE
;
2778 Document
* theDoc
= mDocViewer
->GetDocument();
2779 if (!theDoc
) return NS_ERROR_FAILURE
;
2781 nsCOMPtr
<nsPIDOMWindowOuter
> domWindow
= theDoc
->GetWindow();
2782 if (!domWindow
) return NS_ERROR_FAILURE
;
2784 bool selectionCollapsed
= selection
->IsCollapsed();
2785 // We only call UpdateCommands when the selection changes from collapsed to
2786 // non-collapsed or vice versa, however we skip the initializing collapse. We
2787 // might need another update string for simple selection changes, but that
2788 // would be expenseive.
2789 if (mSelectionWasCollapsed
!= selectionCollapsed
) {
2790 domWindow
->UpdateCommands(u
"select"_ns
);
2791 mSelectionWasCollapsed
= selectionCollapsed
;
2797 // nsDocViewerFocusListener
2798 NS_IMPL_ISUPPORTS(nsDocViewerFocusListener
, nsIDOMEventListener
)
2800 nsresult
nsDocViewerFocusListener::HandleEvent(Event
* aEvent
) {
2801 NS_ENSURE_STATE(mDocViewer
);
2803 RefPtr
<PresShell
> presShell
= mDocViewer
->GetPresShell();
2804 NS_ENSURE_TRUE(presShell
, NS_ERROR_FAILURE
);
2806 RefPtr
<nsFrameSelection
> selection
=
2807 presShell
->GetLastFocusedFrameSelection();
2808 NS_ENSURE_TRUE(selection
, NS_ERROR_FAILURE
);
2809 auto selectionStatus
= selection
->GetDisplaySelection();
2810 nsAutoString eventType
;
2811 aEvent
->GetType(eventType
);
2812 if (eventType
.EqualsLiteral("focus")) {
2813 // If selection was disabled, re-enable it.
2814 if (selectionStatus
== nsISelectionController::SELECTION_DISABLED
||
2815 selectionStatus
== nsISelectionController::SELECTION_HIDDEN
) {
2816 selection
->SetDisplaySelection(nsISelectionController::SELECTION_ON
);
2817 selection
->RepaintSelection(SelectionType::eNormal
);
2819 // See EditorBase::FinalizeSelection. This fixes up the case where focus
2820 // left the editor's selection but returned to something else.
2821 if (selection
!= presShell
->ConstFrameSelection()) {
2822 RefPtr
<Document
> doc
= presShell
->GetDocument();
2823 const bool selectionMatchesFocus
=
2824 selection
->GetLimiter() &&
2825 selection
->GetLimiter()->GetChromeOnlyAccessSubtreeRootParent() ==
2826 doc
->GetUnretargetedFocusedContent();
2827 if (NS_WARN_IF(!selectionMatchesFocus
)) {
2828 presShell
->FrameSelectionWillLoseFocus(*selection
);
2829 presShell
->SelectionWillTakeFocus();
2833 MOZ_ASSERT(eventType
.EqualsLiteral("blur"), "Unexpected event type");
2834 // If selection was on, disable it.
2835 if (selectionStatus
== nsISelectionController::SELECTION_ON
||
2836 selectionStatus
== nsISelectionController::SELECTION_ATTENTION
) {
2837 selection
->SetDisplaySelection(
2838 nsISelectionController::SELECTION_DISABLED
);
2839 selection
->RepaintSelection(SelectionType::eNormal
);
2846 /** ---------------------------------------------------
2847 * From nsIWebBrowserPrint
2853 nsDocumentViewer::Print(nsIPrintSettings
* aPrintSettings
,
2854 RemotePrintJobChild
* aRemotePrintJob
,
2855 nsIWebProgressListener
* aWebProgressListener
) {
2856 if (NS_WARN_IF(!mContainer
)) {
2857 PR_PL(("Container was destroyed yet we are still trying to use it!"));
2858 return NS_ERROR_FAILURE
;
2861 if (NS_WARN_IF(!mDocument
) || NS_WARN_IF(!mDeviceContext
)) {
2862 PR_PL(("Can't Print without a document and a device context"));
2863 return NS_ERROR_FAILURE
;
2866 if (NS_WARN_IF(mPrintJob
&& mPrintJob
->GetIsPrinting())) {
2867 // If we are printing another URL, then exit.
2868 // The reason we check here is because this method can be called while
2869 // another is still in here (the printing dialog is a good example). the
2870 // only time we can print more than one job at a time is the regression
2872 nsresult rv
= NS_ERROR_NOT_AVAILABLE
;
2873 RefPtr
<nsPrintJob
>(mPrintJob
)->FirePrintingErrorEvent(rv
);
2879 // Note: mContainer and mDocument are known to be non-null via null-checks
2880 // earlier in this function.
2881 // TODO(dholbert) Do we need to bother with this stack-owned local RefPtr?
2882 // (Is there an edge case where it's needed to keep the nsPrintJob alive?)
2883 RefPtr
<nsPrintJob
> printJob
=
2884 new nsPrintJob(*this, *mContainer
, *mDocument
,
2885 float(AppUnitsPerCSSInch()) /
2886 float(mDeviceContext
->AppUnitsPerDevPixel()));
2887 mPrintJob
= printJob
;
2889 nsresult rv
= printJob
->Print(*mDocument
, aPrintSettings
, aRemotePrintJob
,
2890 aWebProgressListener
);
2891 if (NS_WARN_IF(NS_FAILED(rv
))) {
2898 nsDocumentViewer::PrintPreview(nsIPrintSettings
* aPrintSettings
,
2899 nsIWebProgressListener
* aWebProgressListener
,
2900 PrintPreviewResolver
&& aCallback
) {
2901 # ifdef NS_PRINT_PREVIEW
2902 RefPtr
<Document
> doc
= mDocument
.get();
2903 NS_ENSURE_STATE(doc
);
2905 if (NS_WARN_IF(GetIsPrinting())) {
2906 return NS_ERROR_FAILURE
;
2909 nsCOMPtr
<nsIDocShell
> docShell(mContainer
);
2910 if (NS_WARN_IF(!docShell
) || NS_WARN_IF(!mDeviceContext
)) {
2911 PR_PL(("Can't Print Preview without device context and docshell"));
2912 return NS_ERROR_FAILURE
;
2915 NS_ENSURE_STATE(!GetIsPrinting());
2916 // beforeprint event may have caused ContentViewer to be shutdown.
2917 NS_ENSURE_STATE(mContainer
);
2918 NS_ENSURE_STATE(mDeviceContext
);
2922 // Note: mContainer and doc are known to be non-null via null-checks earlier
2923 // in this function.
2924 // TODO(dholbert) Do we need to bother with this stack-owned local RefPtr?
2925 // (Is there an edge case where it's needed to keep the nsPrintJob alive?)
2926 RefPtr
<nsPrintJob
> printJob
=
2927 new nsPrintJob(*this, *mContainer
, *doc
,
2928 float(AppUnitsPerCSSInch()) /
2929 float(mDeviceContext
->AppUnitsPerDevPixel()));
2930 mPrintJob
= printJob
;
2932 nsresult rv
= printJob
->PrintPreview(
2933 *doc
, aPrintSettings
, aWebProgressListener
, std::move(aCallback
));
2934 if (NS_WARN_IF(NS_FAILED(rv
))) {
2939 return NS_ERROR_FAILURE
;
2940 # endif // NS_PRINT_PREVIEW
2943 static const nsIFrame
* GetTargetPageFrame(int32_t aTargetPageNum
,
2944 nsPageSequenceFrame
* aSequenceFrame
) {
2945 MOZ_ASSERT(aTargetPageNum
> 0 &&
2947 aSequenceFrame
->PrincipalChildList().GetLength());
2948 return aSequenceFrame
->PrincipalChildList().FrameAt(aTargetPageNum
- 1);
2951 // Calculate the scroll position where the center of |aFrame| is positioned at
2952 // the center of |aScrollable|'s scroll port for the print preview.
2953 // So what we do for that is;
2954 // 1) Calculate the position of the center of |aFrame| in the print preview
2956 // 2) Reduce the half height of the scroll port from the result of 1.
2957 static nscoord
ScrollPositionForFrame(const nsIFrame
* aFrame
,
2958 nsIScrollableFrame
* aScrollable
,
2959 float aPreviewScale
) {
2960 // Note that even if the computed scroll position is out of the range of
2961 // the scroll port, it gets clamped in nsIScrollableFrame::ScrollTo.
2962 return nscoord(aPreviewScale
* aFrame
->GetRect().Center().y
-
2963 float(aScrollable
->GetScrollPortRect().height
) / 2.0f
);
2966 //----------------------------------------------------------------------
2968 nsDocumentViewer::PrintPreviewScrollToPage(int16_t aType
, int32_t aPageNum
) {
2969 if (!GetIsPrintPreview() || mPrintJob
->GetIsCreatingPrintPreview())
2970 return NS_ERROR_FAILURE
;
2972 nsIScrollableFrame
* sf
= mPresShell
->GetRootScrollFrameAsScrollable();
2973 if (!sf
) return NS_OK
;
2975 auto [seqFrame
, sheetCount
] = mPrintJob
->GetSeqFrameAndCountSheets();
2976 Unused
<< sheetCount
;
2978 return NS_ERROR_FAILURE
;
2981 float previewScale
= seqFrame
->GetPrintPreviewScale();
2983 nsPoint dest
= sf
->GetScrollPosition();
2986 case nsIWebBrowserPrint::PRINTPREVIEW_HOME
:
2989 case nsIWebBrowserPrint::PRINTPREVIEW_END
:
2990 dest
.y
= sf
->GetScrollRange().YMost();
2992 case nsIWebBrowserPrint::PRINTPREVIEW_PREV_PAGE
:
2993 case nsIWebBrowserPrint::PRINTPREVIEW_NEXT_PAGE
: {
2994 auto [currentFrame
, currentSheetNumber
] = GetCurrentSheetFrameAndNumber();
2995 Unused
<< currentSheetNumber
;
2996 if (!currentFrame
) {
3000 const nsIFrame
* targetFrame
= nullptr;
3001 if (aType
== nsIWebBrowserPrint::PRINTPREVIEW_PREV_PAGE
) {
3002 targetFrame
= currentFrame
->GetPrevInFlow();
3004 targetFrame
= currentFrame
->GetNextInFlow();
3010 dest
.y
= ScrollPositionForFrame(targetFrame
, sf
, previewScale
);
3013 case nsIWebBrowserPrint::PRINTPREVIEW_GOTO_PAGENUM
: {
3014 if (aPageNum
<= 0 || aPageNum
> sheetCount
) {
3015 return NS_ERROR_INVALID_ARG
;
3018 const nsIFrame
* targetFrame
= GetTargetPageFrame(aPageNum
, seqFrame
);
3019 MOZ_ASSERT(targetFrame
);
3021 dest
.y
= ScrollPositionForFrame(targetFrame
, sf
, previewScale
);
3025 return NS_ERROR_INVALID_ARG
;
3029 sf
->ScrollTo(dest
, ScrollMode::Instant
);
3034 std::tuple
<const nsIFrame
*, int32_t>
3035 nsDocumentViewer::GetCurrentSheetFrameAndNumber() const {
3036 MOZ_ASSERT(mPrintJob
);
3037 MOZ_ASSERT(GetIsPrintPreview() && !mPrintJob
->GetIsCreatingPrintPreview());
3039 // in PP mPrtPreview->mPrintObject->mSeqFrame is null
3040 auto [seqFrame
, sheetCount
] = mPrintJob
->GetSeqFrameAndCountSheets();
3041 Unused
<< sheetCount
;
3043 return {nullptr, 0};
3046 nsIScrollableFrame
* sf
= mPresShell
->GetRootScrollFrameAsScrollable();
3048 // No scrollable contents, returns 1 even if there are multiple sheets.
3049 return {seqFrame
->PrincipalChildList().FirstChild(), 1};
3052 nsPoint currentScrollPosition
= sf
->GetScrollPosition();
3053 float halfwayPoint
=
3054 currentScrollPosition
.y
+ float(sf
->GetScrollPortRect().height
) / 2.0f
;
3055 float lastDistanceFromHalfwayPoint
= std::numeric_limits
<float>::max();
3056 int32_t sheetNumber
= 0;
3057 const nsIFrame
* currentSheet
= nullptr;
3058 float previewScale
= seqFrame
->GetPrintPreviewScale();
3059 for (const nsIFrame
* sheetFrame
: seqFrame
->PrincipalChildList()) {
3060 nsRect sheetRect
= sheetFrame
->GetRect();
3062 currentSheet
= sheetFrame
;
3064 float bottomOfSheet
= sheetRect
.YMost() * previewScale
;
3065 if (bottomOfSheet
< halfwayPoint
) {
3066 // If the bottom of the sheet is not yet over the halfway point, iterate
3067 // the next frame to see if the next frame is over the halfway point and
3068 // compare the distance from the halfway point.
3069 lastDistanceFromHalfwayPoint
= halfwayPoint
- bottomOfSheet
;
3073 float topOfSheet
= sheetRect
.Y() * previewScale
;
3074 if (topOfSheet
<= halfwayPoint
) {
3075 // If the top of the sheet is not yet over the halfway point or on the
3076 // point, it's the current sheet.
3080 // Now the sheet rect is completely over the halfway point, compare the
3081 // distances from the halfway point.
3082 if ((topOfSheet
- halfwayPoint
) >= lastDistanceFromHalfwayPoint
) {
3083 // If the previous sheet distance is less than or equal to the current
3084 // sheet distance, choose the previous one as the current.
3086 MOZ_ASSERT(sheetNumber
> 0);
3087 currentSheet
= currentSheet
->GetPrevInFlow();
3088 MOZ_ASSERT(currentSheet
);
3093 MOZ_ASSERT(sheetNumber
<= sheetCount
);
3094 return {currentSheet
, sheetNumber
};
3097 // XXXdholbert As noted in nsIWebBrowserPrint.idl, this API (the IDL attr
3098 // 'printPreviewCurrentPageNumber') is misnamed and needs s/Page/Sheet/. See
3101 nsDocumentViewer::GetPrintPreviewCurrentPageNumber(int32_t* aNumber
) {
3102 NS_ENSURE_ARG_POINTER(aNumber
);
3103 NS_ENSURE_TRUE(mPrintJob
, NS_ERROR_FAILURE
);
3104 if (!GetIsPrintPreview() || mPrintJob
->GetIsCreatingPrintPreview()) {
3105 return NS_ERROR_FAILURE
;
3108 auto [currentFrame
, currentSheetNumber
] = GetCurrentSheetFrameAndNumber();
3109 Unused
<< currentFrame
;
3110 if (!currentSheetNumber
) {
3111 return NS_ERROR_FAILURE
;
3114 *aNumber
= currentSheetNumber
;
3119 // XXX This always returns false for subdocuments
3121 nsDocumentViewer::GetDoingPrint(bool* aDoingPrint
) {
3122 NS_ENSURE_ARG_POINTER(aDoingPrint
);
3124 // XXX shouldn't this be GetDoingPrint() ?
3125 *aDoingPrint
= mPrintJob
? mPrintJob
->CreatedForPrintPreview() : false;
3129 // XXX This always returns false for subdocuments
3131 nsDocumentViewer::GetDoingPrintPreview(bool* aDoingPrintPreview
) {
3132 NS_ENSURE_ARG_POINTER(aDoingPrintPreview
);
3134 *aDoingPrintPreview
= mPrintJob
? mPrintJob
->CreatedForPrintPreview() : false;
3139 nsDocumentViewer::ExitPrintPreview() {
3140 NS_ENSURE_TRUE(mPrintJob
, NS_ERROR_FAILURE
);
3142 if (GetIsPrinting()) {
3143 // Block exiting the print preview window if we're in the middle of an
3145 return NS_ERROR_FAILURE
;
3148 if (!GetIsPrintPreview()) {
3149 NS_ERROR("Wow, we should never get here!");
3153 # ifdef NS_PRINT_PREVIEW
3154 mPrintJob
->Destroy();
3155 mPrintJob
= nullptr;
3157 // Since the print preview implementation discards the window that was used
3158 // to show the print preview, we skip certain cleanup that we would otherwise
3159 // want to do. Specifically, we do not call `SetIsPrintPreview(false)` to
3160 // unblock navigation, we do not call `SetOverrideDPPX` to reset the
3161 // devicePixelRatio, and we do not call `Show` to make such changes take
3163 # endif // NS_PRINT_PREVIEW
3169 nsDocumentViewer::GetRawNumPages(int32_t* aRawNumPages
) {
3170 NS_ENSURE_ARG_POINTER(aRawNumPages
);
3171 NS_ENSURE_TRUE(mPrintJob
, NS_ERROR_FAILURE
);
3173 *aRawNumPages
= mPrintJob
->GetRawNumPages();
3174 return *aRawNumPages
> 0 ? NS_OK
: NS_ERROR_FAILURE
;
3177 // XXXdholbert As noted in nsIWebBrowserPrint.idl, this API (the IDL attr
3178 // 'printPreviewNumPages') is misnamed and needs s/Page/Sheet/.
3181 nsDocumentViewer::GetPrintPreviewNumPages(int32_t* aPrintPreviewNumPages
) {
3182 NS_ENSURE_ARG_POINTER(aPrintPreviewNumPages
);
3183 NS_ENSURE_TRUE(mPrintJob
, NS_ERROR_FAILURE
);
3184 *aPrintPreviewNumPages
= mPrintJob
->GetPrintPreviewNumSheets();
3185 return *aPrintPreviewNumPages
> 0 ? NS_OK
: NS_ERROR_FAILURE
;
3188 //----------------------------------------------------------------------------------
3189 // Printing/Print Preview Helpers
3190 //----------------------------------------------------------------------------------
3192 //----------------------------------------------------------------------------------
3193 // Walks the document tree and tells each DocShell whether Printing/PP is
3195 #endif // NS_PRINTING
3197 bool nsDocumentViewer::ShouldAttachToTopLevel() {
3198 if (!mParentWidget
) {
3202 // We always attach when using puppet widgets
3203 if (nsIWidget::UsePuppetWidgets()) {
3207 // FIXME(emilio): Can we unify this between macOS and aother platforms?
3212 nsIWidgetListener
* parentListener
= mParentWidget
->GetWidgetListener();
3213 MOZ_ASSERT(!parentListener
|| !parentListener
->GetView(),
3214 "Expect a top level widget");
3220 //------------------------------------------------------------
3221 // XXX this always returns false for subdocuments
3222 bool nsDocumentViewer::GetIsPrinting() const {
3225 return mPrintJob
->GetIsPrinting();
3231 //------------------------------------------------------------
3232 // The PrintJob holds the current value
3233 // this called from inside the DocViewer.
3234 // XXX it always returns false for subdocuments
3235 bool nsDocumentViewer::GetIsPrintPreview() const {
3237 return mPrintJob
&& mPrintJob
->CreatedForPrintPreview();
3243 //------------------------------------------------------------
3244 // Notification from the PrintJob of the current PP status
3245 void nsDocumentViewer::SetIsPrintPreview(bool aIsPrintPreview
) {
3246 // Protect against pres shell destruction running scripts.
3247 nsAutoScriptBlocker scriptBlocker
;
3249 if (!aIsPrintPreview
) {
3250 InvalidatePotentialSubDocDisplayItem();
3255 mViewManager
= nullptr;
3256 mPresContext
= nullptr;
3257 mPresShell
= nullptr;
3261 //----------------------------------------------------------------------------------
3262 // nsIDocumentViewerPrint IFace
3263 //----------------------------------------------------------------------------------
3265 //------------------------------------------------------------
3266 void nsDocumentViewer::IncrementDestroyBlockedCount() {
3267 ++mDestroyBlockedCount
;
3270 void nsDocumentViewer::DecrementDestroyBlockedCount() {
3271 --mDestroyBlockedCount
;
3274 //------------------------------------------------------------
3275 // This called ONLY when printing has completed and the DV
3276 // is being notified that it should get rid of the nsPrintJob.
3278 // BUT, if we are in Print Preview then we want to ignore the
3279 // notification (we do not get rid of the nsPrintJob)
3281 // One small caveat:
3282 // This IS called from two places in this module for cleaning
3283 // up when an error occurred during the start up printing
3284 // and print preview
3286 void nsDocumentViewer::OnDonePrinting() {
3287 #if defined(NS_PRINTING) && defined(NS_PRINT_PREVIEW)
3288 // If Destroy() has been called during calling nsPrintJob::Print() or
3289 // nsPrintJob::PrintPreview(), mPrintJob is already nullptr here.
3290 // So, the following clean up does nothing in such case.
3291 // (Do we need some of this for that case?)
3293 RefPtr
<nsPrintJob
> printJob
= std::move(mPrintJob
);
3294 if (GetIsPrintPreview()) {
3295 printJob
->DestroyPrintingData();
3297 printJob
->Destroy();
3300 // We are done printing, now clean up.
3302 // For non-print-preview jobs, we are actually responsible for cleaning up
3303 // our whole <browser> or window (see the OPEN_PRINT_BROWSER code), so gotta
3304 // run window.close(), which will take care of this.
3306 // For print preview jobs the front-end code is responsible for cleaning the
3308 if (!printJob
->CreatedForPrintPreview()) {
3310 if (nsCOMPtr
<nsPIDOMWindowOuter
> win
= mContainer
->GetWindow()) {
3314 } else if (mClosingWhilePrinting
) {
3316 mDocument
->Destroy();
3317 mDocument
= nullptr;
3319 mClosingWhilePrinting
= false;
3322 #endif // NS_PRINTING && NS_PRINT_PREVIEW
3325 NS_IMETHODIMP
nsDocumentViewer::SetPrintSettingsForSubdocument(
3326 nsIPrintSettings
* aPrintSettings
, RemotePrintJobChild
* aRemotePrintJob
) {
3329 nsAutoScriptBlocker scriptBlocker
;
3336 DestroyPresContext();
3339 MOZ_ASSERT(!mPresContext
);
3340 MOZ_ASSERT(!mPresShell
);
3342 if (MOZ_UNLIKELY(!mDocument
)) {
3343 return NS_ERROR_NOT_AVAILABLE
;
3346 RefPtr
<nsDeviceContextSpecProxy
> devspec
=
3347 new nsDeviceContextSpecProxy(aRemotePrintJob
);
3348 nsresult rv
= devspec
->Init(aPrintSettings
, /* aIsPrintPreview = */ true);
3349 NS_ENSURE_SUCCESS(rv
, rv
);
3351 mDeviceContext
= new nsDeviceContext();
3352 rv
= mDeviceContext
->InitForPrinting(devspec
);
3354 NS_ENSURE_SUCCESS(rv
, rv
);
3356 mPresContext
= CreatePresContext(
3357 mDocument
, nsPresContext::eContext_PrintPreview
, FindContainerView());
3358 mPresContext
->SetPrintSettings(aPrintSettings
);
3359 rv
= mPresContext
->Init(mDeviceContext
);
3360 NS_ENSURE_SUCCESS(rv
, rv
);
3362 rv
= MakeWindow(nsSize(mPresContext
->DevPixelsToAppUnits(mBounds
.width
),
3363 mPresContext
->DevPixelsToAppUnits(mBounds
.height
)),
3364 FindContainerView());
3365 NS_ENSURE_SUCCESS(rv
, rv
);
3367 MOZ_TRY(InitPresentationStuff(true));
3370 RefPtr
<PresShell
> shell
= mPresShell
;
3371 shell
->FlushPendingNotifications(FlushType::Layout
);
3376 NS_IMETHODIMP
nsDocumentViewer::SetPageModeForTesting(
3377 bool aPageMode
, nsIPrintSettings
* aPrintSettings
) {
3378 // XXX Page mode is only partially working; it's currently used for
3379 // reftests that require a paginated context
3380 mIsPageMode
= aPageMode
;
3382 // The DestroyPresShell call requires a script blocker, since the
3383 // PresShell::Destroy call it does can cause scripts to run, which could
3384 // re-entrantly call methods on the nsDocumentViewer.
3385 nsAutoScriptBlocker scriptBlocker
;
3392 DestroyPresContext();
3395 mViewManager
= nullptr;
3398 NS_ENSURE_STATE(mDocument
);
3400 mPresContext
= CreatePresContext(
3401 mDocument
, nsPresContext::eContext_PageLayout
, FindContainerView());
3402 NS_ENSURE_TRUE(mPresContext
, NS_ERROR_OUT_OF_MEMORY
);
3403 mPresContext
->SetPaginatedScrolling(true);
3404 mPresContext
->SetPrintSettings(aPrintSettings
);
3405 nsresult rv
= mPresContext
->Init(mDeviceContext
);
3406 NS_ENSURE_SUCCESS(rv
, rv
);
3408 NS_ENSURE_SUCCESS(InitInternal(mParentWidget
, nullptr, nullptr, mBounds
, true,
3417 nsDocumentViewer::GetHistoryEntry(nsISHEntry
** aHistoryEntry
) {
3418 NS_IF_ADDREF(*aHistoryEntry
= mSHEntry
);
3423 nsDocumentViewer::GetIsTabModalPromptAllowed(bool* aAllowed
) {
3424 *aAllowed
= !mHidden
;
3429 nsDocumentViewer::GetIsHidden(bool* aHidden
) {
3435 nsDocumentViewer::SetIsHidden(bool aHidden
) {
3440 void nsDocumentViewer::DestroyPresShell() {
3441 // We assert this because destroying the pres shell could otherwise cause
3442 // re-entrancy into nsDocumentViewer methods, and all callers of
3443 // DestroyPresShell need to do other cleanup work afterwards before it
3444 // is safe for those re-entrant method calls to be made.
3445 MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(),
3446 "DestroyPresShell must only be called when scripts are blocked");
3448 // Break circular reference (or something)
3449 mPresShell
->EndObservingDocument();
3451 RefPtr
<mozilla::dom::Selection
> selection
= GetDocumentSelection();
3452 if (selection
&& mSelectionListener
)
3453 selection
->RemoveSelectionListener(mSelectionListener
);
3455 mPresShell
->Destroy();
3456 mPresShell
= nullptr;
3459 void nsDocumentViewer::InvalidatePotentialSubDocDisplayItem() {
3461 if (nsView
* rootView
= mViewManager
->GetRootView()) {
3462 if (nsView
* rootViewParent
= rootView
->GetParent()) {
3463 if (nsView
* subdocview
= rootViewParent
->GetParent()) {
3464 if (nsIFrame
* f
= subdocview
->GetFrame()) {
3465 if (nsSubDocumentFrame
* s
= do_QueryFrame(f
)) {
3466 s
->MarkNeedsDisplayItemRebuild();
3475 void nsDocumentViewer::DestroyPresContext() {
3476 InvalidatePotentialSubDocDisplayItem();
3477 mPresContext
= nullptr;
3480 void nsDocumentViewer::SetPrintPreviewPresentation(nsViewManager
* aViewManager
,
3481 nsPresContext
* aPresContext
,
3482 PresShell
* aPresShell
) {
3483 // Protect against pres shell destruction running scripts and re-entrantly
3484 // creating a new presentation.
3485 nsAutoScriptBlocker scriptBlocker
;
3492 mViewManager
= aViewManager
;
3493 mPresContext
= aPresContext
;
3494 mPresShell
= aPresShell
;
3496 if (ShouldAttachToTopLevel()) {
3497 DetachFromTopLevelWidget();
3498 nsView
* rootView
= mViewManager
->GetRootView();
3499 rootView
->AttachToTopLevelWidget(mParentWidget
);
3500 mAttachedToParent
= true;
3504 // Fires the "document-shown" event so that interested parties are aware of it.
3506 nsDocumentShownDispatcher::Run() {
3507 nsCOMPtr
<nsIObserverService
> observerService
=
3508 mozilla::services::GetObserverService();
3509 if (observerService
) {
3510 observerService
->NotifyObservers(ToSupports(mDocument
), "document-shown",