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