mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 02:09:05 +02:00 
			
		
		
		
	Depends on D170367 Differential Revision: https://phabricator.services.mozilla.com/D170369
		
			
				
	
	
		
			3568 lines
		
	
	
	
		
			113 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			3568 lines
		
	
	
	
		
			113 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | 
						|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | 
						|
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
/* container for a document and its presentation */
 | 
						|
 | 
						|
#include "gfxContext.h"
 | 
						|
#include "mozilla/PresShell.h"
 | 
						|
#include "mozilla/RestyleManager.h"
 | 
						|
#include "mozilla/ServoStyleSet.h"
 | 
						|
#include "mozilla/StaticPrefs_print.h"
 | 
						|
#include "mozilla/Telemetry.h"
 | 
						|
#include "nsThreadUtils.h"
 | 
						|
#include "nscore.h"
 | 
						|
#include "nsCOMPtr.h"
 | 
						|
#include "nsCRT.h"
 | 
						|
#include "nsFrameSelection.h"
 | 
						|
#include "nsString.h"
 | 
						|
#include "nsReadableUtils.h"
 | 
						|
#include "nsIContent.h"
 | 
						|
#include "nsIContentViewer.h"
 | 
						|
#include "nsIDocumentViewerPrint.h"
 | 
						|
#include "nsIScreen.h"
 | 
						|
#include "mozilla/dom/AutoSuppressEventHandlingAndSuspend.h"
 | 
						|
#include "mozilla/dom/BrowsingContext.h"
 | 
						|
#include "mozilla/dom/BeforeUnloadEvent.h"
 | 
						|
#include "mozilla/dom/PopupBlocker.h"
 | 
						|
#include "mozilla/dom/Document.h"
 | 
						|
#include "mozilla/dom/DocumentInlines.h"
 | 
						|
#include "mozilla/dom/DocGroup.h"
 | 
						|
#include "mozilla/widget/Screen.h"
 | 
						|
#include "nsPresContext.h"
 | 
						|
#include "nsIFrame.h"
 | 
						|
#include "nsIWritablePropertyBag2.h"
 | 
						|
#include "nsSubDocumentFrame.h"
 | 
						|
#include "nsGenericHTMLElement.h"
 | 
						|
#include "nsStubMutationObserver.h"
 | 
						|
 | 
						|
#include "nsISelectionListener.h"
 | 
						|
#include "mozilla/dom/Selection.h"
 | 
						|
#include "nsContentUtils.h"
 | 
						|
#ifdef ACCESSIBILITY
 | 
						|
#  include "mozilla/a11y/DocAccessible.h"
 | 
						|
#endif
 | 
						|
#include "mozilla/BasicEvents.h"
 | 
						|
#include "mozilla/Encoding.h"
 | 
						|
#include "mozilla/ErrorResult.h"
 | 
						|
#include "mozilla/Preferences.h"
 | 
						|
#include "mozilla/SpinEventLoopUntil.h"
 | 
						|
#include "mozilla/WeakPtr.h"
 | 
						|
#include "mozilla/StaticPrefs_dom.h"
 | 
						|
#include "mozilla/StaticPrefs_javascript.h"
 | 
						|
#include "mozilla/StaticPrefs_fission.h"
 | 
						|
#include "mozilla/StaticPrefs_print.h"
 | 
						|
#include "mozilla/StyleSheet.h"
 | 
						|
#include "mozilla/StyleSheetInlines.h"
 | 
						|
 | 
						|
#include "nsViewManager.h"
 | 
						|
#include "nsView.h"
 | 
						|
 | 
						|
#include "nsPageSequenceFrame.h"
 | 
						|
#include "nsNetUtil.h"
 | 
						|
#include "nsIContentViewerEdit.h"
 | 
						|
#include "mozilla/css/Loader.h"
 | 
						|
#include "nsIInterfaceRequestor.h"
 | 
						|
#include "nsIInterfaceRequestorUtils.h"
 | 
						|
#include "nsDocShell.h"
 | 
						|
#include "nsIBaseWindow.h"
 | 
						|
#include "nsILayoutHistoryState.h"
 | 
						|
#include "nsCharsetSource.h"
 | 
						|
#include "mozilla/ReflowInput.h"
 | 
						|
#include "nsIImageLoadingContent.h"
 | 
						|
#include "nsCopySupport.h"
 | 
						|
#include "nsXULPopupManager.h"
 | 
						|
 | 
						|
#include "nsIClipboardHelper.h"
 | 
						|
 | 
						|
#include "nsPIDOMWindow.h"
 | 
						|
#include "nsGlobalWindow.h"
 | 
						|
#include "nsDOMNavigationTiming.h"
 | 
						|
#include "nsPIWindowRoot.h"
 | 
						|
#include "nsJSEnvironment.h"
 | 
						|
#include "nsFocusManager.h"
 | 
						|
 | 
						|
#include "nsIScrollableFrame.h"
 | 
						|
#include "nsStyleSheetService.h"
 | 
						|
#include "nsILoadContext.h"
 | 
						|
#include "mozilla/ThrottledEventQueue.h"
 | 
						|
#include "nsIPromptCollection.h"
 | 
						|
#include "nsIPromptService.h"
 | 
						|
#include "imgIContainer.h"  // image animation mode constants
 | 
						|
#include "nsIXULRuntime.h"
 | 
						|
#include "nsSandboxFlags.h"
 | 
						|
 | 
						|
#include "mozilla/DocLoadingTimelineMarker.h"
 | 
						|
 | 
						|
//--------------------------
 | 
						|
// Printing Include
 | 
						|
//---------------------------
 | 
						|
#ifdef NS_PRINTING
 | 
						|
 | 
						|
#  include "nsIWebBrowserPrint.h"
 | 
						|
 | 
						|
#  include "nsPrintJob.h"
 | 
						|
#  include "nsDeviceContextSpecProxy.h"
 | 
						|
 | 
						|
// Print Options
 | 
						|
#  include "nsIPrintSettings.h"
 | 
						|
#  include "nsIPrintSettingsService.h"
 | 
						|
#  include "nsISimpleEnumerator.h"
 | 
						|
 | 
						|
#endif  // NS_PRINTING
 | 
						|
 | 
						|
// focus
 | 
						|
#include "nsIDOMEventListener.h"
 | 
						|
#include "nsISelectionController.h"
 | 
						|
 | 
						|
#include "mozilla/EventDispatcher.h"
 | 
						|
#include "nsISHEntry.h"
 | 
						|
#include "nsISHistory.h"
 | 
						|
#include "nsIWebNavigation.h"
 | 
						|
#include "mozilla/dom/XMLHttpRequestMainThread.h"
 | 
						|
 | 
						|
// paint forcing
 | 
						|
#include <stdio.h>
 | 
						|
#include "mozilla/BasePrincipal.h"
 | 
						|
#include "mozilla/dom/Element.h"
 | 
						|
#include "mozilla/dom/Event.h"
 | 
						|
#include "mozilla/Telemetry.h"
 | 
						|
#include "mozilla/dom/ScriptLoader.h"
 | 
						|
#include "mozilla/dom/WindowGlobalChild.h"
 | 
						|
 | 
						|
namespace mozilla {
 | 
						|
namespace dom {
 | 
						|
class PrintPreviewResultInfo;
 | 
						|
}  // namespace dom
 | 
						|
}  // namespace mozilla
 | 
						|
 | 
						|
using namespace mozilla;
 | 
						|
using namespace mozilla::dom;
 | 
						|
 | 
						|
using mozilla::layout::RemotePrintJobChild;
 | 
						|
using PrintPreviewResolver =
 | 
						|
    std::function<void(const mozilla::dom::PrintPreviewResultInfo&)>;
 | 
						|
 | 
						|
//-----------------------------------------------------
 | 
						|
// LOGGING
 | 
						|
#include "LayoutLogging.h"
 | 
						|
#include "mozilla/Logging.h"
 | 
						|
 | 
						|
extern mozilla::LazyLogModule gPageCacheLog;
 | 
						|
 | 
						|
#ifdef NS_PRINTING
 | 
						|
mozilla::LazyLogModule gPrintingLog("printing");
 | 
						|
 | 
						|
#  define PR_PL(_p1) MOZ_LOG(gPrintingLog, mozilla::LogLevel::Debug, _p1);
 | 
						|
#endif  // NS_PRINTING
 | 
						|
 | 
						|
#define PRT_YESNO(_p) ((_p) ? "YES" : "NO")
 | 
						|
//-----------------------------------------------------
 | 
						|
 | 
						|
class nsDocumentViewer;
 | 
						|
 | 
						|
// a small delegate class used to avoid circular references
 | 
						|
 | 
						|
class nsDocViewerSelectionListener final : public nsISelectionListener {
 | 
						|
 public:
 | 
						|
  // nsISupports interface...
 | 
						|
  NS_DECL_ISUPPORTS
 | 
						|
 | 
						|
  // nsISelectionListerner interface
 | 
						|
  NS_DECL_NSISELECTIONLISTENER
 | 
						|
 | 
						|
  explicit nsDocViewerSelectionListener(nsDocumentViewer* aDocViewer)
 | 
						|
      : mDocViewer(aDocViewer), mSelectionWasCollapsed(true) {}
 | 
						|
 | 
						|
  void Disconnect() { mDocViewer = nullptr; }
 | 
						|
 | 
						|
 protected:
 | 
						|
  virtual ~nsDocViewerSelectionListener() = default;
 | 
						|
 | 
						|
  nsDocumentViewer* mDocViewer;
 | 
						|
  bool mSelectionWasCollapsed;
 | 
						|
};
 | 
						|
 | 
						|
/** editor Implementation of the FocusListener interface */
 | 
						|
class nsDocViewerFocusListener final : public nsIDOMEventListener {
 | 
						|
 public:
 | 
						|
  explicit nsDocViewerFocusListener(nsDocumentViewer* aDocViewer)
 | 
						|
      : mDocViewer(aDocViewer) {}
 | 
						|
 | 
						|
  NS_DECL_ISUPPORTS
 | 
						|
  NS_DECL_NSIDOMEVENTLISTENER
 | 
						|
 | 
						|
  void Disconnect() { mDocViewer = nullptr; }
 | 
						|
 | 
						|
 protected:
 | 
						|
  virtual ~nsDocViewerFocusListener() = default;
 | 
						|
 | 
						|
  nsDocumentViewer* mDocViewer;
 | 
						|
};
 | 
						|
 | 
						|
namespace viewer_detail {
 | 
						|
 | 
						|
/**
 | 
						|
 * Mutation observer for use until we hand ourselves over to our SHEntry.
 | 
						|
 */
 | 
						|
class BFCachePreventionObserver final : public nsStubMutationObserver {
 | 
						|
 public:
 | 
						|
  explicit BFCachePreventionObserver(Document* aDocument)
 | 
						|
      : mDocument(aDocument) {}
 | 
						|
 | 
						|
  NS_DECL_ISUPPORTS
 | 
						|
 | 
						|
  NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
 | 
						|
  NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
 | 
						|
  NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
 | 
						|
  NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
 | 
						|
  NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
 | 
						|
  NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
 | 
						|
 | 
						|
  // Stop observing the document.
 | 
						|
  void Disconnect();
 | 
						|
 | 
						|
 private:
 | 
						|
  ~BFCachePreventionObserver() = default;
 | 
						|
 | 
						|
  // Helper for the work that needs to happen when mutations happen.
 | 
						|
  void MutationHappened();
 | 
						|
 | 
						|
  Document* mDocument;  // Weak; we get notified if it dies
 | 
						|
};
 | 
						|
 | 
						|
NS_IMPL_ISUPPORTS(BFCachePreventionObserver, nsIMutationObserver)
 | 
						|
 | 
						|
void BFCachePreventionObserver::CharacterDataChanged(
 | 
						|
    nsIContent* aContent, const CharacterDataChangeInfo&) {
 | 
						|
  if (aContent->IsInNativeAnonymousSubtree()) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  MutationHappened();
 | 
						|
}
 | 
						|
 | 
						|
void BFCachePreventionObserver::AttributeChanged(Element* aElement,
 | 
						|
                                                 int32_t aNameSpaceID,
 | 
						|
                                                 nsAtom* aAttribute,
 | 
						|
                                                 int32_t aModType,
 | 
						|
                                                 const nsAttrValue* aOldValue) {
 | 
						|
  if (aElement->IsInNativeAnonymousSubtree()) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  MutationHappened();
 | 
						|
}
 | 
						|
 | 
						|
void BFCachePreventionObserver::ContentAppended(nsIContent* aFirstNewContent) {
 | 
						|
  if (aFirstNewContent->IsInNativeAnonymousSubtree()) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  MutationHappened();
 | 
						|
}
 | 
						|
 | 
						|
void BFCachePreventionObserver::ContentInserted(nsIContent* aChild) {
 | 
						|
  if (aChild->IsInNativeAnonymousSubtree()) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  MutationHappened();
 | 
						|
}
 | 
						|
 | 
						|
void BFCachePreventionObserver::ContentRemoved(nsIContent* aChild,
 | 
						|
                                               nsIContent* aPreviousSibling) {
 | 
						|
  if (aChild->IsInNativeAnonymousSubtree()) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  MutationHappened();
 | 
						|
}
 | 
						|
 | 
						|
void BFCachePreventionObserver::NodeWillBeDestroyed(nsINode* aNode) {
 | 
						|
  mDocument = nullptr;
 | 
						|
}
 | 
						|
 | 
						|
void BFCachePreventionObserver::Disconnect() {
 | 
						|
  if (mDocument) {
 | 
						|
    mDocument->RemoveMutationObserver(this);
 | 
						|
    // It will no longer tell us when it goes away, so make sure we're
 | 
						|
    // not holding a dangling ref.
 | 
						|
    mDocument = nullptr;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void BFCachePreventionObserver::MutationHappened() {
 | 
						|
  MOZ_ASSERT(
 | 
						|
      mDocument,
 | 
						|
      "How can we not have a document but be getting notified for mutations?");
 | 
						|
  mDocument->DisallowBFCaching();
 | 
						|
  Disconnect();
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace viewer_detail
 | 
						|
 | 
						|
using viewer_detail::BFCachePreventionObserver;
 | 
						|
 | 
						|
//-------------------------------------------------------------
 | 
						|
class nsDocumentViewer final : public nsIContentViewer,
 | 
						|
                               public nsIContentViewerEdit,
 | 
						|
                               public nsIDocumentViewerPrint
 | 
						|
#ifdef NS_PRINTING
 | 
						|
    ,
 | 
						|
                               public nsIWebBrowserPrint
 | 
						|
#endif
 | 
						|
 | 
						|
{
 | 
						|
  friend class nsDocViewerSelectionListener;
 | 
						|
  friend class nsPagePrintTimer;
 | 
						|
  friend class nsPrintJob;
 | 
						|
 | 
						|
 public:
 | 
						|
  nsDocumentViewer();
 | 
						|
 | 
						|
  // nsISupports interface...
 | 
						|
  NS_DECL_ISUPPORTS
 | 
						|
 | 
						|
  // nsIContentViewer interface...
 | 
						|
  NS_DECL_NSICONTENTVIEWER
 | 
						|
 | 
						|
  // nsIContentViewerEdit
 | 
						|
  NS_DECL_NSICONTENTVIEWEREDIT
 | 
						|
 | 
						|
#ifdef NS_PRINTING
 | 
						|
  // nsIWebBrowserPrint
 | 
						|
  NS_DECL_NSIWEBBROWSERPRINT
 | 
						|
#endif
 | 
						|
 | 
						|
  // nsIDocumentViewerPrint Printing Methods
 | 
						|
  NS_DECL_NSIDOCUMENTVIEWERPRINT
 | 
						|
 | 
						|
 protected:
 | 
						|
  virtual ~nsDocumentViewer();
 | 
						|
 | 
						|
 private:
 | 
						|
  /**
 | 
						|
   * Creates a view manager, root view, and widget for the root view, setting
 | 
						|
   * mViewManager and mWindow.
 | 
						|
   * @param aSize the initial size in appunits
 | 
						|
   * @param aContainerView the container view to hook our root view up
 | 
						|
   * to as a child, or null if this will be the root view manager
 | 
						|
   */
 | 
						|
  nsresult MakeWindow(const nsSize& aSize, nsView* aContainerView);
 | 
						|
 | 
						|
  /**
 | 
						|
   * Create our device context
 | 
						|
   */
 | 
						|
  nsresult CreateDeviceContext(nsView* aContainerView);
 | 
						|
 | 
						|
  /**
 | 
						|
   * If aDoCreation is true, this creates the device context, creates a
 | 
						|
   * prescontext if necessary, and calls MakeWindow.
 | 
						|
   *
 | 
						|
   * If aForceSetNewDocument is false, then SetNewDocument won't be
 | 
						|
   * called if the window's current document is already mDocument.
 | 
						|
   */
 | 
						|
  nsresult InitInternal(nsIWidget* aParentWidget, nsISupports* aState,
 | 
						|
                        mozilla::dom::WindowGlobalChild* aActor,
 | 
						|
                        const nsIntRect& aBounds, bool aDoCreation,
 | 
						|
                        bool aNeedMakeCX = true,
 | 
						|
                        bool aForceSetNewDocument = true);
 | 
						|
  /**
 | 
						|
   * @param aDoInitialReflow set to true if you want to kick off the initial
 | 
						|
   * reflow
 | 
						|
   */
 | 
						|
  MOZ_CAN_RUN_SCRIPT_BOUNDARY
 | 
						|
  nsresult InitPresentationStuff(bool aDoInitialReflow);
 | 
						|
 | 
						|
  already_AddRefed<nsINode> GetPopupNode();
 | 
						|
  already_AddRefed<nsINode> GetPopupLinkNode();
 | 
						|
  already_AddRefed<nsIImageLoadingContent> GetPopupImageNode();
 | 
						|
 | 
						|
  void PrepareToStartLoad(void);
 | 
						|
 | 
						|
  nsresult SyncParentSubDocMap();
 | 
						|
 | 
						|
  void RemoveFocusListener();
 | 
						|
  void ReinitializeFocusListener();
 | 
						|
 | 
						|
  mozilla::dom::Selection* GetDocumentSelection();
 | 
						|
 | 
						|
  void DestroyPresShell();
 | 
						|
  void DestroyPresContext();
 | 
						|
 | 
						|
  void InvalidatePotentialSubDocDisplayItem();
 | 
						|
 | 
						|
  // Whether we should attach to the top level widget. This is true if we
 | 
						|
  // are sharing/recycling a single base widget and not creating multiple
 | 
						|
  // child widgets.
 | 
						|
  bool ShouldAttachToTopLevel();
 | 
						|
 | 
						|
  std::tuple<const nsIFrame*, int32_t> GetCurrentSheetFrameAndNumber() const;
 | 
						|
 | 
						|
 protected:
 | 
						|
  // Returns the current viewmanager.  Might be null.
 | 
						|
  nsViewManager* GetViewManager();
 | 
						|
 | 
						|
  void DetachFromTopLevelWidget();
 | 
						|
 | 
						|
  // IMPORTANT: The ownership implicit in the following member
 | 
						|
  // variables has been explicitly checked and set using nsCOMPtr
 | 
						|
  // for owning pointers and raw COM interface pointers for weak
 | 
						|
  // (ie, non owning) references. If you add any members to this
 | 
						|
  // class, please make the ownership explicit (pinkerton, scc).
 | 
						|
 | 
						|
  WeakPtr<nsDocShell> mContainer;          // it owns me!
 | 
						|
  RefPtr<nsDeviceContext> mDeviceContext;  // We create and own this baby
 | 
						|
 | 
						|
  // the following six items are explicitly in this order
 | 
						|
  // so they will be destroyed in the reverse order (pinkerton, scc)
 | 
						|
  nsCOMPtr<Document> mDocument;
 | 
						|
  nsCOMPtr<nsIWidget> mWindow;  // may be null
 | 
						|
  RefPtr<nsViewManager> mViewManager;
 | 
						|
  RefPtr<nsPresContext> mPresContext;
 | 
						|
  RefPtr<PresShell> mPresShell;
 | 
						|
 | 
						|
  RefPtr<nsDocViewerSelectionListener> mSelectionListener;
 | 
						|
  RefPtr<nsDocViewerFocusListener> mFocusListener;
 | 
						|
 | 
						|
  nsCOMPtr<nsIContentViewer> mPreviousViewer;
 | 
						|
  nsCOMPtr<nsISHEntry> mSHEntry;
 | 
						|
  // Observer that will prevent bfcaching if it gets notified.  This
 | 
						|
  // is non-null precisely when mSHEntry is non-null.
 | 
						|
  RefPtr<BFCachePreventionObserver> mBFCachePreventionObserver;
 | 
						|
 | 
						|
  nsIWidget* mParentWidget;  // purposely won't be ref counted.  May be null
 | 
						|
  bool mAttachedToParent;    // view is attached to the parent widget
 | 
						|
 | 
						|
  nsIntRect mBounds;
 | 
						|
 | 
						|
  int16_t mNumURLStarts;
 | 
						|
  int16_t mDestroyBlockedCount;
 | 
						|
 | 
						|
  unsigned mStopped : 1;
 | 
						|
  unsigned mLoaded : 1;
 | 
						|
  unsigned mDeferredWindowClose : 1;
 | 
						|
  // document management data
 | 
						|
  //   these items are specific to markup documents (html and xml)
 | 
						|
  //   may consider splitting these out into a subclass
 | 
						|
  unsigned mIsSticky : 1;
 | 
						|
  unsigned mInPermitUnload : 1;
 | 
						|
  unsigned mInPermitUnloadPrompt : 1;
 | 
						|
 | 
						|
#ifdef NS_PRINTING
 | 
						|
  unsigned mClosingWhilePrinting : 1;
 | 
						|
 | 
						|
#  if NS_PRINT_PREVIEW
 | 
						|
  RefPtr<nsPrintJob> mPrintJob;
 | 
						|
#  endif  // NS_PRINT_PREVIEW
 | 
						|
 | 
						|
#endif  // NS_PRINTING
 | 
						|
 | 
						|
  /* character set member data */
 | 
						|
  int32_t mReloadEncodingSource;
 | 
						|
  const Encoding* mReloadEncoding;
 | 
						|
 | 
						|
  bool mIsPageMode;
 | 
						|
  bool mInitializedForPrintPreview;
 | 
						|
  bool mHidden;
 | 
						|
};
 | 
						|
 | 
						|
class nsDocumentShownDispatcher : public Runnable {
 | 
						|
 public:
 | 
						|
  explicit nsDocumentShownDispatcher(nsCOMPtr<Document> aDocument)
 | 
						|
      : Runnable("nsDocumentShownDispatcher"), mDocument(aDocument) {}
 | 
						|
 | 
						|
  NS_IMETHOD Run() override;
 | 
						|
 | 
						|
 private:
 | 
						|
  nsCOMPtr<Document> mDocument;
 | 
						|
};
 | 
						|
 | 
						|
//------------------------------------------------------------------
 | 
						|
// nsDocumentViewer
 | 
						|
//------------------------------------------------------------------
 | 
						|
 | 
						|
//------------------------------------------------------------------
 | 
						|
already_AddRefed<nsIContentViewer> NS_NewContentViewer() {
 | 
						|
  RefPtr<nsDocumentViewer> viewer = new nsDocumentViewer();
 | 
						|
  return viewer.forget();
 | 
						|
}
 | 
						|
 | 
						|
void nsDocumentViewer::PrepareToStartLoad() {
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(!GetIsPrintPreview(),
 | 
						|
                        "Print preview tab should never navigate");
 | 
						|
 | 
						|
  mStopped = false;
 | 
						|
  mLoaded = false;
 | 
						|
  mAttachedToParent = false;
 | 
						|
  mDeferredWindowClose = false;
 | 
						|
 | 
						|
#ifdef NS_PRINTING
 | 
						|
  mClosingWhilePrinting = false;
 | 
						|
 | 
						|
  // Make sure we have destroyed it and cleared the data member
 | 
						|
  if (mPrintJob) {
 | 
						|
    mPrintJob->Destroy();
 | 
						|
    mPrintJob = nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
#endif  // NS_PRINTING
 | 
						|
}
 | 
						|
 | 
						|
nsDocumentViewer::nsDocumentViewer()
 | 
						|
    : mParentWidget(nullptr),
 | 
						|
      mAttachedToParent(false),
 | 
						|
      mNumURLStarts(0),
 | 
						|
      mDestroyBlockedCount(0),
 | 
						|
      mStopped(false),
 | 
						|
      mLoaded(false),
 | 
						|
      mDeferredWindowClose(false),
 | 
						|
      mIsSticky(true),
 | 
						|
      mInPermitUnload(false),
 | 
						|
      mInPermitUnloadPrompt(false),
 | 
						|
#ifdef NS_PRINTING
 | 
						|
      mClosingWhilePrinting(false),
 | 
						|
#endif  // NS_PRINTING
 | 
						|
      mReloadEncodingSource(kCharsetUninitialized),
 | 
						|
      mReloadEncoding(nullptr),
 | 
						|
      mIsPageMode(false),
 | 
						|
      mInitializedForPrintPreview(false),
 | 
						|
      mHidden(false) {
 | 
						|
  PrepareToStartLoad();
 | 
						|
}
 | 
						|
 | 
						|
NS_IMPL_ADDREF(nsDocumentViewer)
 | 
						|
NS_IMPL_RELEASE(nsDocumentViewer)
 | 
						|
 | 
						|
NS_INTERFACE_MAP_BEGIN(nsDocumentViewer)
 | 
						|
  NS_INTERFACE_MAP_ENTRY(nsIContentViewer)
 | 
						|
  NS_INTERFACE_MAP_ENTRY(nsIContentViewerEdit)
 | 
						|
  NS_INTERFACE_MAP_ENTRY(nsIDocumentViewerPrint)
 | 
						|
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentViewer)
 | 
						|
#ifdef NS_PRINTING
 | 
						|
  NS_INTERFACE_MAP_ENTRY(nsIWebBrowserPrint)
 | 
						|
#endif
 | 
						|
NS_INTERFACE_MAP_END
 | 
						|
 | 
						|
nsDocumentViewer::~nsDocumentViewer() {
 | 
						|
  if (mDocument) {
 | 
						|
    Close(nullptr);
 | 
						|
    mDocument->Destroy();
 | 
						|
  }
 | 
						|
 | 
						|
#ifdef NS_PRINTING
 | 
						|
  if (mPrintJob) {
 | 
						|
    mPrintJob->Destroy();
 | 
						|
    mPrintJob = nullptr;
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  MOZ_RELEASE_ASSERT(mDestroyBlockedCount == 0);
 | 
						|
  NS_ASSERTION(!mPresShell && !mPresContext,
 | 
						|
               "User did not call nsIContentViewer::Destroy");
 | 
						|
  if (mPresShell || mPresContext) {
 | 
						|
    // Make sure we don't hand out a reference to the content viewer to
 | 
						|
    // the SHEntry!
 | 
						|
    mSHEntry = nullptr;
 | 
						|
 | 
						|
    Destroy();
 | 
						|
  }
 | 
						|
 | 
						|
  if (mSelectionListener) {
 | 
						|
    mSelectionListener->Disconnect();
 | 
						|
  }
 | 
						|
 | 
						|
  RemoveFocusListener();
 | 
						|
 | 
						|
  // XXX(?) Revoke pending invalidate events
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * This method is called by the Document Loader once a document has
 | 
						|
 * been created for a particular data stream...  The content viewer
 | 
						|
 * must cache this document for later use when Init(...) is called.
 | 
						|
 *
 | 
						|
 * This method is also called when an out of band document.write() happens.
 | 
						|
 * In that case, the document passed in is the same as the previous document.
 | 
						|
 */
 | 
						|
/* virtual */
 | 
						|
void nsDocumentViewer::LoadStart(Document* aDocument) {
 | 
						|
  MOZ_ASSERT(aDocument);
 | 
						|
 | 
						|
  if (!mDocument) {
 | 
						|
    mDocument = aDocument;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void nsDocumentViewer::RemoveFocusListener() {
 | 
						|
  if (RefPtr<nsDocViewerFocusListener> oldListener =
 | 
						|
          std::move(mFocusListener)) {
 | 
						|
    oldListener->Disconnect();
 | 
						|
    if (mDocument) {
 | 
						|
      mDocument->RemoveEventListener(u"focus"_ns, oldListener, false);
 | 
						|
      mDocument->RemoveEventListener(u"blur"_ns, oldListener, false);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void nsDocumentViewer::ReinitializeFocusListener() {
 | 
						|
  RemoveFocusListener();
 | 
						|
  mFocusListener = new nsDocViewerFocusListener(this);
 | 
						|
  if (mDocument) {
 | 
						|
    mDocument->AddEventListener(u"focus"_ns, mFocusListener, false, false);
 | 
						|
    mDocument->AddEventListener(u"blur"_ns, mFocusListener, false, false);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
nsresult nsDocumentViewer::SyncParentSubDocMap() {
 | 
						|
  nsCOMPtr<nsIDocShell> docShell(mContainer);
 | 
						|
  if (!docShell) {
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<nsPIDOMWindowOuter> pwin(docShell->GetWindow());
 | 
						|
  if (!mDocument || !pwin) {
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<Element> element = pwin->GetFrameElementInternal();
 | 
						|
  if (!element) {
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<nsIDocShellTreeItem> parent;
 | 
						|
  docShell->GetInProcessParent(getter_AddRefs(parent));
 | 
						|
 | 
						|
  nsCOMPtr<nsPIDOMWindowOuter> parent_win =
 | 
						|
      parent ? parent->GetWindow() : nullptr;
 | 
						|
  if (!parent_win) {
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<Document> parent_doc = parent_win->GetDoc();
 | 
						|
  if (!parent_doc) {
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  if (mDocument && parent_doc->GetSubDocumentFor(element) != mDocument &&
 | 
						|
      parent_doc->EventHandlingSuppressed()) {
 | 
						|
    mDocument->SuppressEventHandling(parent_doc->EventHandlingSuppressed());
 | 
						|
  }
 | 
						|
  return parent_doc->SetSubDocumentFor(element, mDocument);
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::SetContainer(nsIDocShell* aContainer) {
 | 
						|
  mContainer = static_cast<nsDocShell*>(aContainer);
 | 
						|
 | 
						|
  // We're loading a new document into the window where this document
 | 
						|
  // viewer lives, sync the parent document's frame element -> sub
 | 
						|
  // document map
 | 
						|
 | 
						|
  return SyncParentSubDocMap();
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::GetContainer(nsIDocShell** aResult) {
 | 
						|
  NS_ENSURE_ARG_POINTER(aResult);
 | 
						|
 | 
						|
  nsCOMPtr<nsIDocShell> container(mContainer);
 | 
						|
  container.swap(*aResult);
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::Init(nsIWidget* aParentWidget, const nsIntRect& aBounds,
 | 
						|
                       WindowGlobalChild* aActor) {
 | 
						|
  return InitInternal(aParentWidget, nullptr, aActor, aBounds, true);
 | 
						|
}
 | 
						|
 | 
						|
nsresult nsDocumentViewer::InitPresentationStuff(bool aDoInitialReflow) {
 | 
						|
  // We assert this because initializing the pres shell could otherwise cause
 | 
						|
  // re-entrancy into nsDocumentViewer methods, which might cause a different
 | 
						|
  // pres shell to be created.  Callers of InitPresentationStuff should ensure
 | 
						|
  // the call is appropriately bounded by an nsAutoScriptBlocker to decide
 | 
						|
  // when it is safe for these re-entrant calls to be made.
 | 
						|
  MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(),
 | 
						|
             "InitPresentationStuff must only be called when scripts are "
 | 
						|
             "blocked");
 | 
						|
 | 
						|
#ifdef NS_PRINTING
 | 
						|
  // When getting printed, either for print or print preview, the print job
 | 
						|
  // takes care of setting up the presentation of the document.
 | 
						|
  if (mPrintJob) {
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  NS_ASSERTION(!mPresShell, "Someone should have destroyed the presshell!");
 | 
						|
 | 
						|
  // Now make the shell for the document
 | 
						|
  nsCOMPtr<Document> doc = mDocument;
 | 
						|
  RefPtr<nsPresContext> presContext = mPresContext;
 | 
						|
  RefPtr<nsViewManager> viewManager = mViewManager;
 | 
						|
  mPresShell = doc->CreatePresShell(presContext, viewManager);
 | 
						|
  if (!mPresShell) {
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
 | 
						|
  if (aDoInitialReflow) {
 | 
						|
    // Since Initialize() will create frames for *all* items
 | 
						|
    // that are currently in the document tree, we need to flush
 | 
						|
    // any pending notifications to prevent the content sink from
 | 
						|
    // duplicating layout frames for content it has added to the tree
 | 
						|
    // but hasn't notified the document about. (Bug 154018)
 | 
						|
    //
 | 
						|
    // Note that we are flushing before we add mPresShell as an observer
 | 
						|
    // to avoid bogus notifications.
 | 
						|
    mDocument->FlushPendingNotifications(FlushType::ContentAndNotify);
 | 
						|
  }
 | 
						|
 | 
						|
  mPresShell->BeginObservingDocument();
 | 
						|
 | 
						|
  // Initialize our view manager
 | 
						|
 | 
						|
  {
 | 
						|
    int32_t p2a = mPresContext->AppUnitsPerDevPixel();
 | 
						|
    MOZ_ASSERT(
 | 
						|
        p2a ==
 | 
						|
        mPresContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom());
 | 
						|
 | 
						|
    nscoord width = p2a * mBounds.width;
 | 
						|
    nscoord height = p2a * mBounds.height;
 | 
						|
 | 
						|
    mViewManager->SetWindowDimensions(width, height);
 | 
						|
    mPresContext->SetVisibleArea(nsRect(0, 0, width, height));
 | 
						|
    // We rely on the default zoom not being initialized until here.
 | 
						|
    mPresContext->RecomputeBrowsingContextDependentData();
 | 
						|
  }
 | 
						|
 | 
						|
  if (mWindow && mDocument->IsTopLevelContentDocument()) {
 | 
						|
    // Set initial safe area insets
 | 
						|
    ScreenIntMargin windowSafeAreaInsets;
 | 
						|
    LayoutDeviceIntRect windowRect = mWindow->GetScreenBounds();
 | 
						|
    nsCOMPtr<nsIScreen> screen = mWindow->GetWidgetScreen();
 | 
						|
    if (screen) {
 | 
						|
      windowSafeAreaInsets = nsContentUtils::GetWindowSafeAreaInsets(
 | 
						|
          screen, mWindow->GetSafeAreaInsets(), windowRect);
 | 
						|
    }
 | 
						|
 | 
						|
    mPresContext->SetSafeAreaInsets(windowSafeAreaInsets);
 | 
						|
  }
 | 
						|
 | 
						|
  if (aDoInitialReflow) {
 | 
						|
    RefPtr<PresShell> presShell = mPresShell;
 | 
						|
    // Initial reflow
 | 
						|
    presShell->Initialize();
 | 
						|
  }
 | 
						|
 | 
						|
  // now register ourselves as a selection listener, so that we get
 | 
						|
  // called when the selection changes in the window
 | 
						|
  if (!mSelectionListener) {
 | 
						|
    mSelectionListener = new nsDocViewerSelectionListener(this);
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<mozilla::dom::Selection> selection = GetDocumentSelection();
 | 
						|
  if (!selection) {
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
 | 
						|
  selection->AddSelectionListener(mSelectionListener);
 | 
						|
 | 
						|
  ReinitializeFocusListener();
 | 
						|
 | 
						|
  if (aDoInitialReflow && mDocument) {
 | 
						|
    nsCOMPtr<Document> document = mDocument;
 | 
						|
    document->ScrollToRef();
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
static nsPresContext* CreatePresContext(Document* aDocument,
 | 
						|
                                        nsPresContext::nsPresContextType aType,
 | 
						|
                                        nsView* aContainerView) {
 | 
						|
  if (aContainerView) {
 | 
						|
    return new nsPresContext(aDocument, aType);
 | 
						|
  }
 | 
						|
  return new nsRootPresContext(aDocument, aType);
 | 
						|
}
 | 
						|
 | 
						|
//-----------------------------------------------
 | 
						|
// This method can be used to initial the "presentation"
 | 
						|
// The aDoCreation indicates whether it should create
 | 
						|
// all the new objects or just initialize the existing ones
 | 
						|
nsresult nsDocumentViewer::InitInternal(
 | 
						|
    nsIWidget* aParentWidget, nsISupports* aState, WindowGlobalChild* aActor,
 | 
						|
    const nsIntRect& aBounds, bool aDoCreation, bool aNeedMakeCX /*= true*/,
 | 
						|
    bool aForceSetNewDocument /* = true*/) {
 | 
						|
  // We don't want any scripts to run here. That can cause flushing,
 | 
						|
  // which can cause reentry into initialization of this document viewer,
 | 
						|
  // which would be disastrous.
 | 
						|
  nsAutoScriptBlocker blockScripts;
 | 
						|
 | 
						|
  mParentWidget = aParentWidget;  // not ref counted
 | 
						|
  mBounds = aBounds;
 | 
						|
 | 
						|
  nsresult rv = NS_OK;
 | 
						|
  NS_ENSURE_TRUE(mDocument, NS_ERROR_NULL_POINTER);
 | 
						|
 | 
						|
  nsView* containerView = FindContainerView();
 | 
						|
 | 
						|
  bool makeCX = false;
 | 
						|
  if (aDoCreation) {
 | 
						|
    nsresult rv = CreateDeviceContext(containerView);
 | 
						|
    NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
    // XXXbz this is a nasty hack to do with the fact that we create
 | 
						|
    // presentations both in Init() and in Show()...  Ideally we would only do
 | 
						|
    // it in one place (Show()) and require that callers call init(), open(),
 | 
						|
    // show() in that order or something.
 | 
						|
    if (!mPresContext &&
 | 
						|
        (aParentWidget || containerView || mDocument->IsBeingUsedAsImage() ||
 | 
						|
         (mDocument->GetDisplayDocument() &&
 | 
						|
          mDocument->GetDisplayDocument()->GetPresShell()))) {
 | 
						|
      // Create presentation context
 | 
						|
      if (mIsPageMode) {
 | 
						|
        // Presentation context already created in SetPageModeForTesting which
 | 
						|
        // is calling this method
 | 
						|
      } else {
 | 
						|
        mPresContext = CreatePresContext(
 | 
						|
            mDocument, nsPresContext::eContext_Galley, containerView);
 | 
						|
      }
 | 
						|
      NS_ENSURE_TRUE(mPresContext, NS_ERROR_OUT_OF_MEMORY);
 | 
						|
 | 
						|
      nsresult rv = mPresContext->Init(mDeviceContext);
 | 
						|
      if (NS_FAILED(rv)) {
 | 
						|
        mPresContext = nullptr;
 | 
						|
        return rv;
 | 
						|
      }
 | 
						|
 | 
						|
#if defined(NS_PRINTING) && defined(NS_PRINT_PREVIEW)
 | 
						|
      makeCX = !GetIsPrintPreview() &&
 | 
						|
               aNeedMakeCX;  // needs to be true except when we are already in
 | 
						|
                             // PP or we are enabling/disabling paginated mode.
 | 
						|
#else
 | 
						|
      makeCX = true;
 | 
						|
#endif
 | 
						|
    }
 | 
						|
 | 
						|
    if (mPresContext) {
 | 
						|
      // Create the ViewManager and Root View...
 | 
						|
 | 
						|
      // We must do this before we tell the script global object about
 | 
						|
      // this new document since doing that will cause us to re-enter
 | 
						|
      // into nsSubDocumentFrame code through reflows caused by
 | 
						|
      // FlushPendingNotifications() calls down the road...
 | 
						|
 | 
						|
      rv = MakeWindow(nsSize(mPresContext->DevPixelsToAppUnits(aBounds.width),
 | 
						|
                             mPresContext->DevPixelsToAppUnits(aBounds.height)),
 | 
						|
                      containerView);
 | 
						|
      NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
      Hide();
 | 
						|
 | 
						|
#ifdef NS_PRINT_PREVIEW
 | 
						|
      if (mIsPageMode) {
 | 
						|
        // I'm leaving this in a broken state for the moment; we should
 | 
						|
        // be measuring/scaling with the print device context, not the
 | 
						|
        // screen device context, but this is good enough to allow
 | 
						|
        // printing reftests to work.
 | 
						|
        double pageWidth = 0, pageHeight = 0;
 | 
						|
        mPresContext->GetPrintSettings()->GetEffectivePageSize(&pageWidth,
 | 
						|
                                                               &pageHeight);
 | 
						|
        mPresContext->SetPageSize(
 | 
						|
            nsSize(mPresContext->CSSTwipsToAppUnits(NSToIntFloor(pageWidth)),
 | 
						|
                   mPresContext->CSSTwipsToAppUnits(NSToIntFloor(pageHeight))));
 | 
						|
        mPresContext->SetIsRootPaginatedDocument(true);
 | 
						|
        mPresContext->SetPageScale(1.0f);
 | 
						|
      }
 | 
						|
#endif
 | 
						|
    } else {
 | 
						|
      // Avoid leaking the old viewer.
 | 
						|
      if (mPreviousViewer) {
 | 
						|
        mPreviousViewer->Destroy();
 | 
						|
        mPreviousViewer = nullptr;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<nsIInterfaceRequestor> requestor(mContainer);
 | 
						|
  if (requestor) {
 | 
						|
    // Set script-context-owner in the document
 | 
						|
 | 
						|
    nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(requestor);
 | 
						|
 | 
						|
    if (window) {
 | 
						|
      nsCOMPtr<Document> curDoc = window->GetExtantDoc();
 | 
						|
      if (aForceSetNewDocument || curDoc != mDocument) {
 | 
						|
        rv = window->SetNewDocument(mDocument, aState, false, aActor);
 | 
						|
        if (NS_FAILED(rv)) {
 | 
						|
          Destroy();
 | 
						|
          return rv;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (aDoCreation && mPresContext) {
 | 
						|
    // The ViewManager and Root View was created above (in
 | 
						|
    // MakeWindow())...
 | 
						|
 | 
						|
    rv = InitPresentationStuff(!makeCX);
 | 
						|
  }
 | 
						|
 | 
						|
  return rv;
 | 
						|
}
 | 
						|
 | 
						|
void nsDocumentViewer::SetNavigationTiming(nsDOMNavigationTiming* timing) {
 | 
						|
  NS_ASSERTION(mDocument, "Must have a document to set navigation timing.");
 | 
						|
  if (mDocument) {
 | 
						|
    mDocument->SetNavigationTiming(timing);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// LoadComplete(aStatus)
 | 
						|
//
 | 
						|
//   aStatus - The status returned from loading the document.
 | 
						|
//
 | 
						|
// This method is called by the container when the document has been
 | 
						|
// completely loaded.
 | 
						|
//
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::LoadComplete(nsresult aStatus) {
 | 
						|
  /* We need to protect ourself against auto-destruction in case the
 | 
						|
     window is closed while processing the OnLoad event.  See bug
 | 
						|
     http://bugzilla.mozilla.org/show_bug.cgi?id=78445 for more
 | 
						|
     explanation.
 | 
						|
  */
 | 
						|
  RefPtr<nsDocumentViewer> kungFuDeathGrip(this);
 | 
						|
 | 
						|
  // Flush out layout so it's up-to-date by the time onload is called.
 | 
						|
  // Note that this could destroy the window, so do this before
 | 
						|
  // checking for our mDocument and its window.
 | 
						|
  if (mPresShell && !mStopped) {
 | 
						|
    // Hold strong ref because this could conceivably run script
 | 
						|
    RefPtr<PresShell> presShell = mPresShell;
 | 
						|
    presShell->FlushPendingNotifications(FlushType::Layout);
 | 
						|
  }
 | 
						|
 | 
						|
  nsresult rv = NS_OK;
 | 
						|
  NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
 | 
						|
 | 
						|
  // First, get the window from the document...
 | 
						|
  nsCOMPtr<nsPIDOMWindowOuter> window = mDocument->GetWindow();
 | 
						|
 | 
						|
  mLoaded = true;
 | 
						|
 | 
						|
  // Now, fire either an OnLoad or OnError event to the document...
 | 
						|
  bool restoring = false;
 | 
						|
  // XXXbz imagelib kills off the document load for a full-page image with
 | 
						|
  // NS_ERROR_PARSED_DATA_CACHED if it's in the cache.  So we want to treat
 | 
						|
  // that one as a success code; otherwise whether we fire onload for the image
 | 
						|
  // will depend on whether it's cached!
 | 
						|
  if (window &&
 | 
						|
      (NS_SUCCEEDED(aStatus) || aStatus == NS_ERROR_PARSED_DATA_CACHED)) {
 | 
						|
    // If this code changes, the code in nsDocLoader::DocLoaderIsEmpty
 | 
						|
    // that fires load events for document.open() cases might need to
 | 
						|
    // be updated too.
 | 
						|
    nsEventStatus status = nsEventStatus_eIgnore;
 | 
						|
    WidgetEvent event(true, eLoad);
 | 
						|
    event.mFlags.mBubbles = false;
 | 
						|
    event.mFlags.mCancelable = false;
 | 
						|
    // XXX Dispatching to |window|, but using |document| as the target.
 | 
						|
    event.mTarget = mDocument;
 | 
						|
 | 
						|
    // If the document presentation is being restored, we don't want to fire
 | 
						|
    // onload to the document content since that would likely confuse scripts
 | 
						|
    // on the page.
 | 
						|
 | 
						|
    RefPtr<nsDocShell> docShell = nsDocShell::Cast(window->GetDocShell());
 | 
						|
    NS_ENSURE_TRUE(docShell, NS_ERROR_UNEXPECTED);
 | 
						|
 | 
						|
    // Unfortunately, docShell->GetRestoringDocument() might no longer be set
 | 
						|
    // correctly.  In particular, it can be false by now if someone took it upon
 | 
						|
    // themselves to block onload from inside restoration and unblock it later.
 | 
						|
    // But we can detect the restoring case very simply: by whether our
 | 
						|
    // document's readyState is COMPLETE.
 | 
						|
    restoring =
 | 
						|
        (mDocument->GetReadyStateEnum() == Document::READYSTATE_COMPLETE);
 | 
						|
    if (!restoring) {
 | 
						|
      NS_ASSERTION(
 | 
						|
          mDocument->GetReadyStateEnum() == Document::READYSTATE_INTERACTIVE ||
 | 
						|
              // test_stricttransportsecurity.html has old-style
 | 
						|
              // docshell-generated about:blank docs reach this code!
 | 
						|
              (mDocument->GetReadyStateEnum() ==
 | 
						|
                   Document::READYSTATE_UNINITIALIZED &&
 | 
						|
               NS_IsAboutBlank(mDocument->GetDocumentURI())),
 | 
						|
          "Bad readystate");
 | 
						|
#ifdef DEBUG
 | 
						|
      bool docShellThinksWeAreRestoring;
 | 
						|
      docShell->GetRestoringDocument(&docShellThinksWeAreRestoring);
 | 
						|
      MOZ_ASSERT(!docShellThinksWeAreRestoring,
 | 
						|
                 "How can docshell think we are restoring if we don't have a "
 | 
						|
                 "READYSTATE_COMPLETE document?");
 | 
						|
#endif  // DEBUG
 | 
						|
      nsCOMPtr<Document> d = mDocument;
 | 
						|
      mDocument->SetReadyStateInternal(Document::READYSTATE_COMPLETE);
 | 
						|
 | 
						|
      RefPtr<nsDOMNavigationTiming> timing(d->GetNavigationTiming());
 | 
						|
      if (timing) {
 | 
						|
        timing->NotifyLoadEventStart();
 | 
						|
      }
 | 
						|
 | 
						|
      // Dispatch observer notification to notify observers document load is
 | 
						|
      // complete.
 | 
						|
      nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
 | 
						|
      if (os) {
 | 
						|
        nsIPrincipal* principal = d->NodePrincipal();
 | 
						|
        os->NotifyObservers(ToSupports(d),
 | 
						|
                            principal->IsSystemPrincipal()
 | 
						|
                                ? "chrome-document-loaded"
 | 
						|
                                : "content-document-loaded",
 | 
						|
                            nullptr);
 | 
						|
      }
 | 
						|
 | 
						|
      // Notify any devtools about the load.
 | 
						|
      if (TimelineConsumers::HasConsumer(docShell)) {
 | 
						|
        TimelineConsumers::AddMarkerForDocShell(
 | 
						|
            docShell, MakeUnique<DocLoadingTimelineMarker>("document::Load"));
 | 
						|
      }
 | 
						|
 | 
						|
      nsPIDOMWindowInner* innerWindow = window->GetCurrentInnerWindow();
 | 
						|
      RefPtr<DocGroup> docGroup = d->GetDocGroup();
 | 
						|
      // It is possible that the parent document's load event fires earlier than
 | 
						|
      // childs' load event, and in this case we need to fire some artificial
 | 
						|
      // load events to make the parent thinks the load events for child has
 | 
						|
      // been done
 | 
						|
      if (innerWindow && DocGroup::TryToLoadIframesInBackground()) {
 | 
						|
        nsTArray<nsCOMPtr<nsIDocShell>> docShells;
 | 
						|
        nsCOMPtr<nsIDocShell> container(mContainer);
 | 
						|
        if (container) {
 | 
						|
          int32_t count;
 | 
						|
          container->GetInProcessChildCount(&count);
 | 
						|
          // We first find all background loading iframes that need to
 | 
						|
          // fire artificial load events, and instead of firing them as
 | 
						|
          // soon as we find them, we store them in an array, to prevent
 | 
						|
          // us from skipping some events.
 | 
						|
          for (int32_t i = 0; i < count; ++i) {
 | 
						|
            nsCOMPtr<nsIDocShellTreeItem> child;
 | 
						|
            container->GetInProcessChildAt(i, getter_AddRefs(child));
 | 
						|
            nsCOMPtr<nsIDocShell> childIDocShell = do_QueryInterface(child);
 | 
						|
            RefPtr<nsDocShell> docShell = nsDocShell::Cast(childIDocShell);
 | 
						|
            if (docShell && docShell->TreatAsBackgroundLoad() &&
 | 
						|
                docShell->GetDocument()->GetReadyStateEnum() <
 | 
						|
                    Document::READYSTATE_COMPLETE) {
 | 
						|
              docShells.AppendElement(childIDocShell);
 | 
						|
            }
 | 
						|
          }
 | 
						|
 | 
						|
          // Re-iterate the stored docShells to fire artificial load events
 | 
						|
          for (size_t i = 0; i < docShells.Length(); ++i) {
 | 
						|
            RefPtr<nsDocShell> docShell = nsDocShell::Cast(docShells[i]);
 | 
						|
            if (docShell && docShell->TreatAsBackgroundLoad() &&
 | 
						|
                docShell->GetDocument()->GetReadyStateEnum() <
 | 
						|
                    Document::READYSTATE_COMPLETE) {
 | 
						|
              nsEventStatus status = nsEventStatus_eIgnore;
 | 
						|
              WidgetEvent event(true, eLoad);
 | 
						|
              event.mFlags.mBubbles = false;
 | 
						|
              event.mFlags.mCancelable = false;
 | 
						|
 | 
						|
              nsCOMPtr<nsPIDOMWindowOuter> win = docShell->GetWindow();
 | 
						|
              nsCOMPtr<Element> element = win->GetFrameElementInternal();
 | 
						|
 | 
						|
              docShell->SetFakeOnLoadDispatched();
 | 
						|
              EventDispatcher::Dispatch(element, nullptr, &event, nullptr,
 | 
						|
                                        &status);
 | 
						|
            }
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      d->SetLoadEventFiring(true);
 | 
						|
      RefPtr<nsPresContext> presContext = mPresContext;
 | 
						|
      EventDispatcher::Dispatch(window, presContext, &event, nullptr, &status);
 | 
						|
      d->SetLoadEventFiring(false);
 | 
						|
 | 
						|
      if (docGroup && docShell->TreatAsBackgroundLoad()) {
 | 
						|
        docGroup->TryFlushIframePostMessages(docShell->GetOuterWindowID());
 | 
						|
      }
 | 
						|
 | 
						|
      if (timing) {
 | 
						|
        timing->NotifyLoadEventEnd();
 | 
						|
      }
 | 
						|
 | 
						|
      if (innerWindow) {
 | 
						|
        innerWindow->QueuePerformanceNavigationTiming();
 | 
						|
      }
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    // XXX: Should fire error event to the document...
 | 
						|
 | 
						|
    // If our load was explicitly aborted, then we want to set our
 | 
						|
    // readyState to COMPLETE, and fire a readystatechange event.
 | 
						|
    if (aStatus == NS_BINDING_ABORTED && mDocument) {
 | 
						|
      mDocument->NotifyAbortedLoad();
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // Notify the document that it has been shown (regardless of whether
 | 
						|
  // it was just loaded). Note: mDocument may be null now if the above
 | 
						|
  // firing of onload caused the document to unload. Or, mDocument may not be
 | 
						|
  // the "current active" document, if the above firing of onload caused our
 | 
						|
  // docshell to navigate away. NOTE: In this latter scenario, it's likely that
 | 
						|
  // we fired pagehide (when navigating away) without ever having fired
 | 
						|
  // pageshow, and that's pretty broken... Fortunately, this should be rare.
 | 
						|
  // (It requires us to spin the event loop in onload handler, e.g. via sync
 | 
						|
  // XHR, in order for the navigation-away to happen before onload completes.)
 | 
						|
  // We skip firing pageshow if we're currently handling unload, or if loading
 | 
						|
  // was explicitly aborted.
 | 
						|
  if (mDocument && mDocument->IsCurrentActiveDocument() &&
 | 
						|
      aStatus != NS_BINDING_ABORTED) {
 | 
						|
    // Re-get window, since it might have changed during above firing of onload
 | 
						|
    window = mDocument->GetWindow();
 | 
						|
    if (window) {
 | 
						|
      nsIDocShell* docShell = window->GetDocShell();
 | 
						|
      bool isInUnload;
 | 
						|
      if (docShell && NS_SUCCEEDED(docShell->GetIsInUnload(&isInUnload)) &&
 | 
						|
          !isInUnload) {
 | 
						|
        mDocument->OnPageShow(restoring, nullptr);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (!mStopped) {
 | 
						|
    if (mDocument) {
 | 
						|
      nsCOMPtr<Document> document = mDocument;
 | 
						|
      document->ScrollToRef();
 | 
						|
    }
 | 
						|
 | 
						|
    // Now that the document has loaded, we can tell the presshell
 | 
						|
    // to unsuppress painting.
 | 
						|
    if (mPresShell) {
 | 
						|
      RefPtr<PresShell> presShell = mPresShell;
 | 
						|
      presShell->UnsuppressPainting();
 | 
						|
      // mPresShell could have been removed now, see bug 378682/421432
 | 
						|
      if (mPresShell) {
 | 
						|
        mPresShell->LoadComplete();
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (mDocument && !restoring) {
 | 
						|
    mDocument->LoadEventFired();
 | 
						|
  }
 | 
						|
 | 
						|
  // It's probably a good idea to GC soon since we have finished loading.
 | 
						|
  nsJSContext::PokeGC(
 | 
						|
      JS::GCReason::LOAD_END,
 | 
						|
      mDocument ? mDocument->GetWrapperPreserveColor() : nullptr);
 | 
						|
 | 
						|
#ifdef NS_PRINTING
 | 
						|
  // Check to see if someone tried to print during the load
 | 
						|
  if (window) {
 | 
						|
    auto* outerWin = nsGlobalWindowOuter::Cast(window);
 | 
						|
    outerWin->StopDelayingPrintingUntilAfterLoad();
 | 
						|
    if (outerWin->DelayedPrintUntilAfterLoad()) {
 | 
						|
      // We call into the inner because it ensures there's an active document
 | 
						|
      // and such, and it also waits until the whole thing completes, which is
 | 
						|
      // nice because it allows us to close if needed right here.
 | 
						|
      if (RefPtr inner =
 | 
						|
              nsGlobalWindowInner::Cast(window->GetCurrentInnerWindow())) {
 | 
						|
        inner->Print(IgnoreErrors());
 | 
						|
      }
 | 
						|
      if (outerWin->DelayedCloseForPrinting()) {
 | 
						|
        outerWin->Close();
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      MOZ_ASSERT(!outerWin->DelayedCloseForPrinting());
 | 
						|
    }
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  return rv;
 | 
						|
}
 | 
						|
 | 
						|
bool nsDocumentViewer::GetLoadCompleted() { return mLoaded; }
 | 
						|
 | 
						|
bool nsDocumentViewer::GetIsStopped() { return mStopped; }
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::PermitUnload(PermitUnloadAction aAction,
 | 
						|
                               bool* aPermitUnload) {
 | 
						|
  // We're going to be running JS and nested event loops, which could cause our
 | 
						|
  // DocShell to be destroyed. Make sure we stay alive until the end of the
 | 
						|
  // function.
 | 
						|
  RefPtr<nsDocumentViewer> kungFuDeathGrip(this);
 | 
						|
 | 
						|
  if (StaticPrefs::dom_disable_beforeunload()) {
 | 
						|
    aAction = eDontPromptAndUnload;
 | 
						|
  }
 | 
						|
 | 
						|
  *aPermitUnload = true;
 | 
						|
 | 
						|
  RefPtr<BrowsingContext> bc = mContainer->GetBrowsingContext();
 | 
						|
  if (!bc) {
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  // Per spec, we need to increase the ignore-opens-during-unload counter while
 | 
						|
  // dispatching the "beforeunload" event on both the document we're currently
 | 
						|
  // dispatching the event to and the document that we explicitly asked to
 | 
						|
  // unload.
 | 
						|
  IgnoreOpensDuringUnload ignoreOpens(mDocument);
 | 
						|
 | 
						|
  bool foundBlocker = false;
 | 
						|
  bool foundOOPListener = false;
 | 
						|
  bc->PreOrderWalk([&](BrowsingContext* aBC) {
 | 
						|
    if (!aBC->IsInProcess()) {
 | 
						|
      WindowContext* wc = aBC->GetCurrentWindowContext();
 | 
						|
      if (wc && wc->HasBeforeUnload()) {
 | 
						|
        foundOOPListener = true;
 | 
						|
      }
 | 
						|
    } else if (aBC->GetDocShell()) {
 | 
						|
      nsCOMPtr<nsIContentViewer> contentViewer(
 | 
						|
          aBC->GetDocShell()->GetContentViewer());
 | 
						|
      if (contentViewer &&
 | 
						|
          contentViewer->DispatchBeforeUnload() == eRequestBlockNavigation) {
 | 
						|
        foundBlocker = true;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  });
 | 
						|
 | 
						|
  if (!foundOOPListener) {
 | 
						|
    if (!foundBlocker) {
 | 
						|
      return NS_OK;
 | 
						|
    }
 | 
						|
    if (aAction != ePrompt) {
 | 
						|
      *aPermitUnload = aAction == eDontPromptAndUnload;
 | 
						|
      return NS_OK;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // NB: we nullcheck mDocument because it might now be dead as a result of
 | 
						|
  // the event being dispatched.
 | 
						|
  RefPtr<WindowGlobalChild> wgc(mDocument ? mDocument->GetWindowGlobalChild()
 | 
						|
                                          : nullptr);
 | 
						|
  if (!wgc) {
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  nsAutoSyncOperation sync(mDocument, SyncOperationBehavior::eSuspendInput);
 | 
						|
  AutoSuppressEventHandlingAndSuspend seh(bc->Group());
 | 
						|
 | 
						|
  mInPermitUnloadPrompt = true;
 | 
						|
 | 
						|
  bool done = false;
 | 
						|
  wgc->SendCheckPermitUnload(
 | 
						|
      foundBlocker, aAction,
 | 
						|
      [&](bool aPermit) {
 | 
						|
        done = true;
 | 
						|
        *aPermitUnload = aPermit;
 | 
						|
      },
 | 
						|
      [&](auto) {
 | 
						|
        // If the prompt aborted, we tell our consumer that it is not allowed
 | 
						|
        // to unload the page. One reason that prompts abort is that the user
 | 
						|
        // performed some action that caused the page to unload while our prompt
 | 
						|
        // was active. In those cases we don't want our consumer to also unload
 | 
						|
        // the page.
 | 
						|
        //
 | 
						|
        // XXX: Are there other cases where prompts can abort? Is it ok to
 | 
						|
        //      prevent unloading the page in those cases?
 | 
						|
        done = true;
 | 
						|
        *aPermitUnload = false;
 | 
						|
      });
 | 
						|
 | 
						|
  SpinEventLoopUntil("nsDocumentViewer::PermitUnload"_ns,
 | 
						|
                     [&]() { return done; });
 | 
						|
 | 
						|
  mInPermitUnloadPrompt = false;
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
MOZ_CAN_RUN_SCRIPT_BOUNDARY PermitUnloadResult
 | 
						|
nsDocumentViewer::DispatchBeforeUnload() {
 | 
						|
  AutoDontWarnAboutSyncXHR disableSyncXHRWarning;
 | 
						|
 | 
						|
  if (!mDocument || mInPermitUnload || mInPermitUnloadPrompt) {
 | 
						|
    return eAllowNavigation;
 | 
						|
  }
 | 
						|
 | 
						|
  // First, get the script global object from the document...
 | 
						|
  RefPtr<nsGlobalWindowOuter> window =
 | 
						|
      nsGlobalWindowOuter::Cast(mDocument->GetWindow());
 | 
						|
  if (!window) {
 | 
						|
    // This is odd, but not fatal
 | 
						|
    NS_WARNING("window not set for document!");
 | 
						|
    return eAllowNavigation;
 | 
						|
  }
 | 
						|
 | 
						|
  NS_ASSERTION(nsContentUtils::IsSafeToRunScript(), "This is unsafe");
 | 
						|
 | 
						|
  // https://html.spec.whatwg.org/multipage/browsing-the-web.html#prompt-to-unload-a-document
 | 
						|
  // Create an RAII object on mDocument that will increment the
 | 
						|
  // should-ignore-opens-during-unload counter on initialization
 | 
						|
  // and decrement it again when it goes out of score (regardless
 | 
						|
  // of how we exit this function).
 | 
						|
  IgnoreOpensDuringUnload ignoreOpens(mDocument);
 | 
						|
 | 
						|
  // Now, fire an BeforeUnload event to the document and see if it's ok
 | 
						|
  // to unload...
 | 
						|
  nsPresContext* presContext = mDocument->GetPresContext();
 | 
						|
  RefPtr<BeforeUnloadEvent> event =
 | 
						|
      new BeforeUnloadEvent(mDocument, presContext, nullptr);
 | 
						|
  event->InitEvent(u"beforeunload"_ns, false, true);
 | 
						|
 | 
						|
  // Dispatching to |window|, but using |document| as the target.
 | 
						|
  event->SetTarget(mDocument);
 | 
						|
  event->SetTrusted(true);
 | 
						|
 | 
						|
  // In evil cases we might be destroyed while handling the
 | 
						|
  // onbeforeunload event, don't let that happen. (see also bug#331040)
 | 
						|
  RefPtr<nsDocumentViewer> kungFuDeathGrip(this);
 | 
						|
 | 
						|
  {
 | 
						|
    // Never permit popups from the beforeunload handler, no matter
 | 
						|
    // how we get here.
 | 
						|
    AutoPopupStatePusher popupStatePusher(PopupBlocker::openAbused, true);
 | 
						|
 | 
						|
    RefPtr<BrowsingContext> bc = mContainer->GetBrowsingContext();
 | 
						|
    NS_ASSERTION(bc, "should have a browsing context in document viewer");
 | 
						|
 | 
						|
    // Never permit dialogs from the beforeunload handler
 | 
						|
    nsGlobalWindowOuter::TemporarilyDisableDialogs disableDialogs(bc);
 | 
						|
 | 
						|
    Document::PageUnloadingEventTimeStamp timestamp(mDocument);
 | 
						|
 | 
						|
    mInPermitUnload = true;
 | 
						|
    RefPtr<nsPresContext> presContext = mPresContext;
 | 
						|
    // TODO: Bug 1506441
 | 
						|
    EventDispatcher::DispatchDOMEvent(MOZ_KnownLive(ToSupports(window)),
 | 
						|
                                      nullptr, event, presContext, nullptr);
 | 
						|
    mInPermitUnload = false;
 | 
						|
  }
 | 
						|
 | 
						|
  nsAutoString text;
 | 
						|
  event->GetReturnValue(text);
 | 
						|
 | 
						|
  // NB: we nullcheck mDocument because it might now be dead as a result of
 | 
						|
  // the event being dispatched.
 | 
						|
  if (window->AreDialogsEnabled() && mDocument &&
 | 
						|
      !(mDocument->GetSandboxFlags() & SANDBOXED_MODALS) &&
 | 
						|
      (!StaticPrefs::dom_require_user_interaction_for_beforeunload() ||
 | 
						|
       mDocument->UserHasInteracted()) &&
 | 
						|
      (event->WidgetEventPtr()->DefaultPrevented() || !text.IsEmpty())) {
 | 
						|
    return eRequestBlockNavigation;
 | 
						|
  }
 | 
						|
  return eAllowNavigation;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::GetBeforeUnloadFiring(bool* aInEvent) {
 | 
						|
  *aInEvent = mInPermitUnload;
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::GetInPermitUnload(bool* aInEvent) {
 | 
						|
  *aInEvent = mInPermitUnloadPrompt;
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::PageHide(bool aIsUnload) {
 | 
						|
  AutoDontWarnAboutSyncXHR disableSyncXHRWarning;
 | 
						|
 | 
						|
  mHidden = true;
 | 
						|
 | 
						|
  if (!mDocument) {
 | 
						|
    return NS_ERROR_NULL_POINTER;
 | 
						|
  }
 | 
						|
 | 
						|
  if (aIsUnload) {
 | 
						|
    // Poke the GC. The window might be collectable garbage now.
 | 
						|
    nsJSContext::PokeGC(JS::GCReason::PAGE_HIDE,
 | 
						|
                        mDocument->GetWrapperPreserveColor(),
 | 
						|
                        TimeDuration::FromMilliseconds(
 | 
						|
                            StaticPrefs::javascript_options_gc_delay() * 2));
 | 
						|
  }
 | 
						|
 | 
						|
  mDocument->OnPageHide(!aIsUnload, nullptr);
 | 
						|
 | 
						|
  // inform the window so that the focus state is reset.
 | 
						|
  NS_ENSURE_STATE(mDocument);
 | 
						|
  nsPIDOMWindowOuter* window = mDocument->GetWindow();
 | 
						|
  if (window) window->PageHidden();
 | 
						|
 | 
						|
  if (aIsUnload) {
 | 
						|
    // if Destroy() was called during OnPageHide(), mDocument is nullptr.
 | 
						|
    NS_ENSURE_STATE(mDocument);
 | 
						|
 | 
						|
    // First, get the window from the document...
 | 
						|
    RefPtr<nsPIDOMWindowOuter> window = mDocument->GetWindow();
 | 
						|
 | 
						|
    if (!window) {
 | 
						|
      // Fail if no window is available...
 | 
						|
      NS_WARNING("window not set for document!");
 | 
						|
      return NS_ERROR_NULL_POINTER;
 | 
						|
    }
 | 
						|
 | 
						|
    // https://html.spec.whatwg.org/multipage/browsing-the-web.html#unload-a-document
 | 
						|
    // Create an RAII object on mDocument that will increment the
 | 
						|
    // should-ignore-opens-during-unload counter on initialization
 | 
						|
    // and decrement it again when it goes out of scope.
 | 
						|
    IgnoreOpensDuringUnload ignoreOpens(mDocument);
 | 
						|
 | 
						|
    // Now, fire an Unload event to the document...
 | 
						|
    nsEventStatus status = nsEventStatus_eIgnore;
 | 
						|
    WidgetEvent event(true, eUnload);
 | 
						|
    event.mFlags.mBubbles = false;
 | 
						|
    // XXX Dispatching to |window|, but using |document| as the target.
 | 
						|
    event.mTarget = mDocument;
 | 
						|
 | 
						|
    // Never permit popups from the unload handler, no matter how we get
 | 
						|
    // here.
 | 
						|
    AutoPopupStatePusher popupStatePusher(PopupBlocker::openAbused, true);
 | 
						|
 | 
						|
    Document::PageUnloadingEventTimeStamp timestamp(mDocument);
 | 
						|
 | 
						|
    RefPtr<nsPresContext> presContext = mPresContext;
 | 
						|
    EventDispatcher::Dispatch(window, presContext, &event, nullptr, &status);
 | 
						|
  }
 | 
						|
 | 
						|
  // look for open menupopups and close them after the unload event, in case
 | 
						|
  // the unload event listeners open any new popups
 | 
						|
  nsContentUtils::HidePopupsInDocument(mDocument);
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
static void AttachContainerRecurse(nsIDocShell* aShell) {
 | 
						|
  nsCOMPtr<nsIContentViewer> viewer;
 | 
						|
  aShell->GetContentViewer(getter_AddRefs(viewer));
 | 
						|
  if (viewer) {
 | 
						|
    viewer->SetIsHidden(false);
 | 
						|
    Document* doc = viewer->GetDocument();
 | 
						|
    if (doc) {
 | 
						|
      doc->SetContainer(static_cast<nsDocShell*>(aShell));
 | 
						|
    }
 | 
						|
    if (PresShell* presShell = viewer->GetPresShell()) {
 | 
						|
      presShell->SetForwardingContainer(WeakPtr<nsDocShell>());
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // Now recurse through the children
 | 
						|
  int32_t childCount;
 | 
						|
  aShell->GetInProcessChildCount(&childCount);
 | 
						|
  for (int32_t i = 0; i < childCount; ++i) {
 | 
						|
    nsCOMPtr<nsIDocShellTreeItem> childItem;
 | 
						|
    aShell->GetInProcessChildAt(i, getter_AddRefs(childItem));
 | 
						|
    nsCOMPtr<nsIDocShell> shell = do_QueryInterface(childItem);
 | 
						|
    AttachContainerRecurse(shell);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::Open(nsISupports* aState, nsISHEntry* aSHEntry) {
 | 
						|
  NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_INITIALIZED);
 | 
						|
 | 
						|
  if (mDocument) {
 | 
						|
    mDocument->SetContainer(mContainer);
 | 
						|
  }
 | 
						|
 | 
						|
  nsresult rv = InitInternal(mParentWidget, aState, nullptr, mBounds, false);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
  mHidden = false;
 | 
						|
 | 
						|
  if (mPresShell) mPresShell->SetForwardingContainer(WeakPtr<nsDocShell>());
 | 
						|
 | 
						|
  // Rehook the child presentations.  The child shells are still in
 | 
						|
  // session history, so get them from there.
 | 
						|
 | 
						|
  if (aSHEntry) {
 | 
						|
    nsCOMPtr<nsIDocShellTreeItem> item;
 | 
						|
    int32_t itemIndex = 0;
 | 
						|
    while (NS_SUCCEEDED(
 | 
						|
               aSHEntry->ChildShellAt(itemIndex++, getter_AddRefs(item))) &&
 | 
						|
           item) {
 | 
						|
      nsCOMPtr<nsIDocShell> shell = do_QueryInterface(item);
 | 
						|
      AttachContainerRecurse(shell);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  SyncParentSubDocMap();
 | 
						|
 | 
						|
  ReinitializeFocusListener();
 | 
						|
 | 
						|
  // XXX re-enable image animations once that works correctly
 | 
						|
 | 
						|
  PrepareToStartLoad();
 | 
						|
 | 
						|
  // When loading a page from the bfcache with puppet widgets, we do the
 | 
						|
  // widget attachment here (it is otherwise done in MakeWindow, which is
 | 
						|
  // called for non-bfcache pages in the history, but not bfcache pages).
 | 
						|
  // Attachment is necessary, since we get detached when another page
 | 
						|
  // is browsed to. That is, if we are one page A, then when we go to
 | 
						|
  // page B, we detach. So page A's view has no widget. If we then go
 | 
						|
  // back to it, and it is in the bfcache, we will use that view, which
 | 
						|
  // doesn't have a widget. The attach call here will properly attach us.
 | 
						|
  if (nsIWidget::UsePuppetWidgets() && mPresContext &&
 | 
						|
      ShouldAttachToTopLevel()) {
 | 
						|
    // If the old view is already attached to our parent, detach
 | 
						|
    DetachFromTopLevelWidget();
 | 
						|
 | 
						|
    nsViewManager* vm = GetViewManager();
 | 
						|
    MOZ_ASSERT(vm, "no view manager");
 | 
						|
    nsView* v = vm->GetRootView();
 | 
						|
    MOZ_ASSERT(v, "no root view");
 | 
						|
    MOZ_ASSERT(mParentWidget, "no mParentWidget to set");
 | 
						|
    v->AttachToTopLevelWidget(mParentWidget);
 | 
						|
 | 
						|
    mAttachedToParent = true;
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::Close(nsISHEntry* aSHEntry) {
 | 
						|
  // All callers are supposed to call close to break circular
 | 
						|
  // references.  If we do this stuff in the destructor, the
 | 
						|
  // destructor might never be called (especially if we're being
 | 
						|
  // used from JS.
 | 
						|
 | 
						|
  mSHEntry = aSHEntry;
 | 
						|
 | 
						|
  // Close is also needed to disable scripts during paint suppression,
 | 
						|
  // since we transfer the existing global object to the new document
 | 
						|
  // that is loaded.  In the future, the global object may become a proxy
 | 
						|
  // for an object that can be switched in and out so that we don't need
 | 
						|
  // to disable scripts during paint suppression.
 | 
						|
 | 
						|
  if (!mDocument) return NS_OK;
 | 
						|
 | 
						|
  if (mSHEntry) {
 | 
						|
    if (mBFCachePreventionObserver) {
 | 
						|
      mBFCachePreventionObserver->Disconnect();
 | 
						|
    }
 | 
						|
    mBFCachePreventionObserver = new BFCachePreventionObserver(mDocument);
 | 
						|
    mDocument->AddMutationObserver(mBFCachePreventionObserver);
 | 
						|
  }
 | 
						|
 | 
						|
#ifdef NS_PRINTING
 | 
						|
  // A Close was called while we were printing
 | 
						|
  // so don't clear the ScriptGlobalObject
 | 
						|
  // or clear the mDocument below
 | 
						|
  if (mPrintJob && !mClosingWhilePrinting) {
 | 
						|
    mClosingWhilePrinting = true;
 | 
						|
  } else
 | 
						|
#endif
 | 
						|
  {
 | 
						|
    // out of band cleanup of docshell
 | 
						|
    mDocument->SetScriptGlobalObject(nullptr);
 | 
						|
 | 
						|
    if (!mSHEntry && mDocument) mDocument->RemovedFromDocShell();
 | 
						|
  }
 | 
						|
 | 
						|
  RemoveFocusListener();
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
static void DetachContainerRecurse(nsIDocShell* aShell) {
 | 
						|
  // Unhook this docshell's presentation
 | 
						|
  aShell->SynchronizeLayoutHistoryState();
 | 
						|
  nsCOMPtr<nsIContentViewer> viewer;
 | 
						|
  aShell->GetContentViewer(getter_AddRefs(viewer));
 | 
						|
  if (viewer) {
 | 
						|
    if (Document* doc = viewer->GetDocument()) {
 | 
						|
      doc->SetContainer(nullptr);
 | 
						|
    }
 | 
						|
    if (PresShell* presShell = viewer->GetPresShell()) {
 | 
						|
      auto weakShell = static_cast<nsDocShell*>(aShell);
 | 
						|
      presShell->SetForwardingContainer(weakShell);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // Now recurse through the children
 | 
						|
  int32_t childCount;
 | 
						|
  aShell->GetInProcessChildCount(&childCount);
 | 
						|
  for (int32_t i = 0; i < childCount; ++i) {
 | 
						|
    nsCOMPtr<nsIDocShellTreeItem> childItem;
 | 
						|
    aShell->GetInProcessChildAt(i, getter_AddRefs(childItem));
 | 
						|
    nsCOMPtr<nsIDocShell> shell = do_QueryInterface(childItem);
 | 
						|
    DetachContainerRecurse(shell);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::Destroy() {
 | 
						|
  // Don't let the document get unloaded while we are printing.
 | 
						|
  // this could happen if we hit the back button during printing.
 | 
						|
  // We also keep the viewer from being cached in session history, since
 | 
						|
  // we require all documents there to be sanitized.
 | 
						|
  if (mDestroyBlockedCount != 0) {
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
#ifdef NS_PRINTING
 | 
						|
  // Here is where we check to see if the document was still being prepared
 | 
						|
  // for printing when it was asked to be destroy from someone externally
 | 
						|
  // This usually happens if the document is unloaded while the user is in the
 | 
						|
  // Print Dialog
 | 
						|
  //
 | 
						|
  // So we flip the bool to remember that the document is going away
 | 
						|
  // and we can clean up and abort later after returning from the Print Dialog
 | 
						|
  if (mPrintJob && mPrintJob->CheckBeforeDestroy()) {
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  // We want to make sure to disconnect mBFCachePreventionObserver before we
 | 
						|
  // Sanitize() below.
 | 
						|
  if (mBFCachePreventionObserver) {
 | 
						|
    mBFCachePreventionObserver->Disconnect();
 | 
						|
    mBFCachePreventionObserver = nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  if (mSHEntry && mDocument && !mDocument->IsBFCachingAllowed()) {
 | 
						|
    // Just drop the SHEntry now and pretend like we never even tried to bfcache
 | 
						|
    // this viewer.  This should only happen when someone calls
 | 
						|
    // DisallowBFCaching() after CanSavePresentation() already ran.  Ensure that
 | 
						|
    // the SHEntry has no viewer and its state is synced up.  We want to do this
 | 
						|
    // via a stack reference, in case those calls mess with our members.
 | 
						|
    MOZ_LOG(gPageCacheLog, LogLevel::Debug,
 | 
						|
            ("BFCache not allowed, dropping SHEntry"));
 | 
						|
    nsCOMPtr<nsISHEntry> shEntry = std::move(mSHEntry);
 | 
						|
    shEntry->SetContentViewer(nullptr);
 | 
						|
    shEntry->SyncPresentationState();
 | 
						|
  }
 | 
						|
 | 
						|
  // If we were told to put ourselves into session history instead of destroy
 | 
						|
  // the presentation, do that now.
 | 
						|
  if (mSHEntry) {
 | 
						|
    if (mPresShell) mPresShell->Freeze();
 | 
						|
 | 
						|
    // Make sure the presentation isn't torn down by Hide().
 | 
						|
    mSHEntry->SetSticky(mIsSticky);
 | 
						|
    mIsSticky = true;
 | 
						|
 | 
						|
    // Remove our root view from the view hierarchy.
 | 
						|
    if (mPresShell) {
 | 
						|
      nsViewManager* vm = mPresShell->GetViewManager();
 | 
						|
      if (vm) {
 | 
						|
        nsView* rootView = vm->GetRootView();
 | 
						|
 | 
						|
        if (rootView) {
 | 
						|
          nsView* rootViewParent = rootView->GetParent();
 | 
						|
          if (rootViewParent) {
 | 
						|
            nsView* subdocview = rootViewParent->GetParent();
 | 
						|
            if (subdocview) {
 | 
						|
              nsIFrame* f = subdocview->GetFrame();
 | 
						|
              if (f) {
 | 
						|
                nsSubDocumentFrame* s = do_QueryFrame(f);
 | 
						|
                if (s) {
 | 
						|
                  s->ClearDisplayItems();
 | 
						|
                }
 | 
						|
              }
 | 
						|
            }
 | 
						|
            nsViewManager* parentVM = rootViewParent->GetViewManager();
 | 
						|
            if (parentVM) {
 | 
						|
              parentVM->RemoveChild(rootView);
 | 
						|
            }
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    Hide();
 | 
						|
 | 
						|
    // This is after Hide() so that the user doesn't see the inputs clear.
 | 
						|
    if (mDocument) {
 | 
						|
      mDocument->Sanitize();
 | 
						|
    }
 | 
						|
 | 
						|
    // Reverse ownership. Do this *after* calling sanitize so that sanitize
 | 
						|
    // doesn't cause mutations that make the SHEntry drop the presentation
 | 
						|
 | 
						|
    // Grab a reference to mSHEntry before calling into things like
 | 
						|
    // SyncPresentationState that might mess with our members.
 | 
						|
    nsCOMPtr<nsISHEntry> shEntry =
 | 
						|
        std::move(mSHEntry);  // we'll need this below
 | 
						|
 | 
						|
    MOZ_LOG(gPageCacheLog, LogLevel::Debug,
 | 
						|
            ("Storing content viewer into cache entry"));
 | 
						|
    shEntry->SetContentViewer(this);
 | 
						|
 | 
						|
    // Always sync the presentation state.  That way even if someone screws up
 | 
						|
    // and shEntry has no window state at this point we'll be ok; we just won't
 | 
						|
    // cache ourselves.
 | 
						|
    shEntry->SyncPresentationState();
 | 
						|
    // XXX Synchronize layout history state to parent once bfcache is supported
 | 
						|
    //     in session-history-in-parent.
 | 
						|
 | 
						|
    // Shut down accessibility for the document before we start to tear it down.
 | 
						|
#ifdef ACCESSIBILITY
 | 
						|
    if (mPresShell) {
 | 
						|
      a11y::DocAccessible* docAcc = mPresShell->GetDocAccessible();
 | 
						|
      if (docAcc) {
 | 
						|
        docAcc->Shutdown();
 | 
						|
      }
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
    // Break the link from the document/presentation to the docshell, so that
 | 
						|
    // link traversals cannot affect the currently-loaded document.
 | 
						|
    // When the presentation is restored, Open() and InitInternal() will reset
 | 
						|
    // these pointers to their original values.
 | 
						|
 | 
						|
    if (mDocument) {
 | 
						|
      mDocument->SetContainer(nullptr);
 | 
						|
    }
 | 
						|
    if (mPresShell) {
 | 
						|
      mPresShell->SetForwardingContainer(mContainer);
 | 
						|
    }
 | 
						|
 | 
						|
    // Do the same for our children.  Note that we need to get the child
 | 
						|
    // docshells from the SHEntry now; the docshell will have cleared them.
 | 
						|
    nsCOMPtr<nsIDocShellTreeItem> item;
 | 
						|
    int32_t itemIndex = 0;
 | 
						|
    while (NS_SUCCEEDED(
 | 
						|
               shEntry->ChildShellAt(itemIndex++, getter_AddRefs(item))) &&
 | 
						|
           item) {
 | 
						|
      nsCOMPtr<nsIDocShell> shell = do_QueryInterface(item);
 | 
						|
      DetachContainerRecurse(shell);
 | 
						|
    }
 | 
						|
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  // The document was not put in the bfcache
 | 
						|
 | 
						|
  // Protect against pres shell destruction running scripts and re-entrantly
 | 
						|
  // creating a new presentation.
 | 
						|
  nsAutoScriptBlocker scriptBlocker;
 | 
						|
 | 
						|
  if (mPresShell) {
 | 
						|
    DestroyPresShell();
 | 
						|
  }
 | 
						|
  if (mDocument) {
 | 
						|
    mDocument->Destroy();
 | 
						|
    mDocument = nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  // All callers are supposed to call destroy to break circular
 | 
						|
  // references.  If we do this stuff in the destructor, the
 | 
						|
  // destructor might never be called (especially if we're being
 | 
						|
  // used from JS.
 | 
						|
 | 
						|
#ifdef NS_PRINTING
 | 
						|
  if (mPrintJob) {
 | 
						|
    RefPtr<nsPrintJob> printJob = std::move(mPrintJob);
 | 
						|
#  ifdef NS_PRINT_PREVIEW
 | 
						|
    if (printJob->CreatedForPrintPreview()) {
 | 
						|
      printJob->FinishPrintPreview();
 | 
						|
    }
 | 
						|
#  endif
 | 
						|
    printJob->Destroy();
 | 
						|
    MOZ_ASSERT(!mPrintJob,
 | 
						|
               "mPrintJob shouldn't be recreated while destroying it");
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  // Avoid leaking the old viewer.
 | 
						|
  if (mPreviousViewer) {
 | 
						|
    mPreviousViewer->Destroy();
 | 
						|
    mPreviousViewer = nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  mDeviceContext = nullptr;
 | 
						|
 | 
						|
  if (mPresContext) {
 | 
						|
    DestroyPresContext();
 | 
						|
  }
 | 
						|
 | 
						|
  mWindow = nullptr;
 | 
						|
  mViewManager = nullptr;
 | 
						|
  mContainer = WeakPtr<nsDocShell>();
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::Stop(void) {
 | 
						|
  NS_ASSERTION(mDocument, "Stop called too early or too late");
 | 
						|
  if (mDocument) {
 | 
						|
    mDocument->StopDocumentLoad();
 | 
						|
  }
 | 
						|
 | 
						|
  mStopped = true;
 | 
						|
 | 
						|
  if (!mLoaded && mPresShell) {
 | 
						|
    // Well, we might as well paint what we have so far.
 | 
						|
    RefPtr<PresShell> presShell = mPresShell;  // bug 378682
 | 
						|
    presShell->UnsuppressPainting();
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::GetDOMDocument(Document** aResult) {
 | 
						|
  NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
 | 
						|
  nsCOMPtr<Document> document = mDocument;
 | 
						|
  document.forget(aResult);
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
Document* nsDocumentViewer::GetDocument() { return mDocument; }
 | 
						|
 | 
						|
nsresult nsDocumentViewer::SetDocument(Document* aDocument) {
 | 
						|
  // Assumptions:
 | 
						|
  //
 | 
						|
  // 1) this document viewer has been initialized with a call to Init().
 | 
						|
  // 2) the stylesheets associated with the document have been added
 | 
						|
  // to the document.
 | 
						|
 | 
						|
  // XXX Right now, this method assumes that the layout of the current
 | 
						|
  // document hasn't started yet.  More cleanup will probably be
 | 
						|
  // necessary to make this method work for the case when layout *has*
 | 
						|
  // occurred for the current document.
 | 
						|
  // That work can happen when and if it is needed.
 | 
						|
 | 
						|
  if (!aDocument) return NS_ERROR_NULL_POINTER;
 | 
						|
 | 
						|
  return SetDocumentInternal(aDocument, false);
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::SetDocumentInternal(Document* aDocument,
 | 
						|
                                      bool aForceReuseInnerWindow) {
 | 
						|
  MOZ_ASSERT(aDocument);
 | 
						|
 | 
						|
  // Set new container
 | 
						|
  aDocument->SetContainer(mContainer);
 | 
						|
 | 
						|
  if (mDocument != aDocument) {
 | 
						|
    if (aForceReuseInnerWindow) {
 | 
						|
      // Transfer the navigation timing information to the new document, since
 | 
						|
      // we're keeping the same inner and hence should really have the same
 | 
						|
      // timing information.
 | 
						|
      aDocument->SetNavigationTiming(mDocument->GetNavigationTiming());
 | 
						|
    }
 | 
						|
 | 
						|
    if (mDocument &&
 | 
						|
        (mDocument->IsStaticDocument() || aDocument->IsStaticDocument())) {
 | 
						|
      nsContentUtils::AddScriptRunner(NewRunnableMethod(
 | 
						|
          "Document::Destroy", mDocument, &Document::Destroy));
 | 
						|
    }
 | 
						|
 | 
						|
    // Clear the list of old child docshells. Child docshells for the new
 | 
						|
    // document will be constructed as frames are created.
 | 
						|
    if (!aDocument->IsStaticDocument()) {
 | 
						|
      nsCOMPtr<nsIDocShell> node(mContainer);
 | 
						|
      if (node) {
 | 
						|
        int32_t count;
 | 
						|
        node->GetInProcessChildCount(&count);
 | 
						|
        for (int32_t i = 0; i < count; ++i) {
 | 
						|
          nsCOMPtr<nsIDocShellTreeItem> child;
 | 
						|
          node->GetInProcessChildAt(0, getter_AddRefs(child));
 | 
						|
          node->RemoveChild(child);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    // Replace the old document with the new one. Do this only when
 | 
						|
    // the new document really is a new document.
 | 
						|
    mDocument = aDocument;
 | 
						|
 | 
						|
    // Set the script global object on the new document
 | 
						|
    nsCOMPtr<nsPIDOMWindowOuter> window =
 | 
						|
        mContainer ? mContainer->GetWindow() : nullptr;
 | 
						|
    if (window) {
 | 
						|
      nsresult rv =
 | 
						|
          window->SetNewDocument(aDocument, nullptr, aForceReuseInnerWindow);
 | 
						|
      if (NS_FAILED(rv)) {
 | 
						|
        Destroy();
 | 
						|
        return rv;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  nsresult rv = SyncParentSubDocMap();
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
  // Replace the current pres shell with a new shell for the new document
 | 
						|
 | 
						|
  // Protect against pres shell destruction running scripts and re-entrantly
 | 
						|
  // creating a new presentation.
 | 
						|
  nsAutoScriptBlocker scriptBlocker;
 | 
						|
 | 
						|
  if (mPresShell) {
 | 
						|
    DestroyPresShell();
 | 
						|
  }
 | 
						|
 | 
						|
  if (mPresContext) {
 | 
						|
    DestroyPresContext();
 | 
						|
 | 
						|
    mWindow = nullptr;
 | 
						|
    rv = InitInternal(mParentWidget, nullptr, nullptr, mBounds, true, true,
 | 
						|
                      false);
 | 
						|
  }
 | 
						|
 | 
						|
  return rv;
 | 
						|
}
 | 
						|
 | 
						|
PresShell* nsDocumentViewer::GetPresShell() { return mPresShell; }
 | 
						|
 | 
						|
nsPresContext* nsDocumentViewer::GetPresContext() { return mPresContext; }
 | 
						|
 | 
						|
nsViewManager* nsDocumentViewer::GetViewManager() { return mViewManager; }
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::GetBounds(nsIntRect& aResult) {
 | 
						|
  NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
 | 
						|
  aResult = mBounds;
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
nsIContentViewer* nsDocumentViewer::GetPreviousViewer() {
 | 
						|
  return mPreviousViewer;
 | 
						|
}
 | 
						|
 | 
						|
void nsDocumentViewer::SetPreviousViewer(nsIContentViewer* aViewer) {
 | 
						|
  // NOTE:  |Show| sets |mPreviousViewer| to null without calling this
 | 
						|
  // function.
 | 
						|
 | 
						|
  if (aViewer) {
 | 
						|
    NS_ASSERTION(!mPreviousViewer,
 | 
						|
                 "can't set previous viewer when there already is one");
 | 
						|
 | 
						|
    // In a multiple chaining situation (which occurs when running a thrashing
 | 
						|
    // test like i-bench or jrgm's tests with no delay), we can build up a
 | 
						|
    // whole chain of viewers.  In order to avoid this, we always set our
 | 
						|
    // previous viewer to the MOST previous viewer in the chain, and then dump
 | 
						|
    // the intermediate link from the chain.  This ensures that at most only 2
 | 
						|
    // documents are alive and undestroyed at any given time (the one that is
 | 
						|
    // showing and the one that is loading with painting suppressed). It's very
 | 
						|
    // important that if this ever gets changed the code before the
 | 
						|
    // RestorePresentation call in nsDocShell::InternalLoad be changed
 | 
						|
    // accordingly.
 | 
						|
    //
 | 
						|
    // Make sure we hold a strong ref to prevViewer here, since we'll
 | 
						|
    // tell aViewer to drop it.
 | 
						|
    nsCOMPtr<nsIContentViewer> prevViewer = aViewer->GetPreviousViewer();
 | 
						|
    if (prevViewer) {
 | 
						|
      aViewer->SetPreviousViewer(nullptr);
 | 
						|
      aViewer->Destroy();
 | 
						|
      return SetPreviousViewer(prevViewer);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  mPreviousViewer = aViewer;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::SetBoundsWithFlags(const nsIntRect& aBounds,
 | 
						|
                                     uint32_t aFlags) {
 | 
						|
  NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
 | 
						|
 | 
						|
  bool boundsChanged = !mBounds.IsEqualEdges(aBounds);
 | 
						|
  mBounds = aBounds;
 | 
						|
 | 
						|
  if (mWindow && !mAttachedToParent) {
 | 
						|
    // Resize the widget, but don't trigger repaint. Layout will generate
 | 
						|
    // repaint requests during reflow.
 | 
						|
    mWindow->Resize(aBounds.x, aBounds.y, aBounds.width, aBounds.height, false);
 | 
						|
  } else if (mPresContext && mViewManager) {
 | 
						|
    // Ensure presContext's deviceContext is up to date, as we sometimes get
 | 
						|
    // here before a resolution-change notification has been fully handled
 | 
						|
    // during display configuration changes, especially when there are lots
 | 
						|
    // of windows/widgets competing to handle the notifications.
 | 
						|
    // (See bug 1154125.)
 | 
						|
    if (mPresContext->DeviceContext()->CheckDPIChange()) {
 | 
						|
      mPresContext->UIResolutionChangedSync();
 | 
						|
    }
 | 
						|
 | 
						|
    int32_t p2a = mPresContext->AppUnitsPerDevPixel();
 | 
						|
    nscoord width = NSIntPixelsToAppUnits(mBounds.width, p2a);
 | 
						|
    nscoord height = NSIntPixelsToAppUnits(mBounds.height, p2a);
 | 
						|
    nsView* rootView = mViewManager->GetRootView();
 | 
						|
    if (boundsChanged && rootView) {
 | 
						|
      nsRect viewDims = rootView->GetDimensions();
 | 
						|
      // If the view/frame tree and prescontext visible area already has the new
 | 
						|
      // size but we did not, then it's likely that we got reflowed in response
 | 
						|
      // to a call to GetContentSize. Thus there is a disconnect between the
 | 
						|
      // size on the document viewer/docshell/containing widget and view
 | 
						|
      // tree/frame tree/prescontext visible area). SetWindowDimensions compares
 | 
						|
      // to the root view dimenstions to determine if it needs to do anything;
 | 
						|
      // if they are the same as the new size it won't do anything, but we still
 | 
						|
      // need to invalidate because what we want to draw to the screen has
 | 
						|
      // changed.
 | 
						|
      if (viewDims.width == width && viewDims.height == height) {
 | 
						|
        nsIFrame* f = rootView->GetFrame();
 | 
						|
        if (f) {
 | 
						|
          f->InvalidateFrame();
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    mViewManager->SetWindowDimensions(
 | 
						|
        width, height, !!(aFlags & nsIContentViewer::eDelayResize));
 | 
						|
  }
 | 
						|
 | 
						|
  // If there's a previous viewer, it's the one that's actually showing,
 | 
						|
  // so be sure to resize it as well so it paints over the right area.
 | 
						|
  // This may slow down the performance of the new page load, but resize
 | 
						|
  // during load is also probably a relatively unusual condition
 | 
						|
  // relating to things being hidden while something is loaded.  It so
 | 
						|
  // happens that Firefox does this a good bit with its infobar, and it
 | 
						|
  // looks ugly if we don't do this.
 | 
						|
  if (mPreviousViewer) {
 | 
						|
    nsCOMPtr<nsIContentViewer> previousViewer = mPreviousViewer;
 | 
						|
    previousViewer->SetBounds(aBounds);
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::SetBounds(const nsIntRect& aBounds) {
 | 
						|
  return SetBoundsWithFlags(aBounds, 0);
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::Move(int32_t aX, int32_t aY) {
 | 
						|
  NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
 | 
						|
  mBounds.MoveTo(aX, aY);
 | 
						|
  if (mWindow) {
 | 
						|
    mWindow->Move(aX, aY);
 | 
						|
  }
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::Show() {
 | 
						|
  NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
 | 
						|
 | 
						|
  // We don't need the previous viewer anymore since we're not
 | 
						|
  // displaying it.
 | 
						|
  if (mPreviousViewer) {
 | 
						|
    // This little dance *may* only be to keep
 | 
						|
    // PresShell::EndObservingDocument happy, but I'm not sure.
 | 
						|
    nsCOMPtr<nsIContentViewer> prevViewer(mPreviousViewer);
 | 
						|
    mPreviousViewer = nullptr;
 | 
						|
    prevViewer->Destroy();
 | 
						|
 | 
						|
    // Make sure we don't have too many cached ContentViewers
 | 
						|
    nsCOMPtr<nsIDocShellTreeItem> treeItem(mContainer);
 | 
						|
    if (treeItem) {
 | 
						|
      // We need to find the root DocShell since only that object has an
 | 
						|
      // SHistory and we need the SHistory to evict content viewers
 | 
						|
      nsCOMPtr<nsIDocShellTreeItem> root;
 | 
						|
      treeItem->GetInProcessSameTypeRootTreeItem(getter_AddRefs(root));
 | 
						|
      nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(root);
 | 
						|
      RefPtr<ChildSHistory> history = webNav->GetSessionHistory();
 | 
						|
      if (!mozilla::SessionHistoryInParent() && history) {
 | 
						|
        int32_t prevIndex, loadedIndex;
 | 
						|
        nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(treeItem);
 | 
						|
        docShell->GetPreviousEntryIndex(&prevIndex);
 | 
						|
        docShell->GetLoadedEntryIndex(&loadedIndex);
 | 
						|
        MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
 | 
						|
                ("About to evict content viewers: prev=%d, loaded=%d",
 | 
						|
                 prevIndex, loadedIndex));
 | 
						|
        history->LegacySHistory()->EvictOutOfRangeContentViewers(loadedIndex);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (mWindow) {
 | 
						|
    // When attached to a top level xul window, we do not need to call
 | 
						|
    // Show on the widget. Underlying window management code handles
 | 
						|
    // this when the window is initialized.
 | 
						|
    if (!mAttachedToParent) {
 | 
						|
      mWindow->Show(true);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // Hold on to the document so we can use it after the script blocker below
 | 
						|
  // has been released (which might re-entrantly call into other
 | 
						|
  // nsDocumentViewer methods).
 | 
						|
  nsCOMPtr<Document> document = mDocument;
 | 
						|
 | 
						|
  if (mDocument && !mPresShell) {
 | 
						|
    // The InitPresentationStuff call below requires a script blocker, because
 | 
						|
    // its PresShell::Initialize call can cause scripts to run and therefore
 | 
						|
    // re-entrant calls to nsDocumentViewer methods to be made.
 | 
						|
    nsAutoScriptBlocker scriptBlocker;
 | 
						|
 | 
						|
    NS_ASSERTION(!mWindow, "Window already created but no presshell?");
 | 
						|
 | 
						|
    nsCOMPtr<nsIBaseWindow> base_win(mContainer);
 | 
						|
    if (base_win) {
 | 
						|
      base_win->GetParentWidget(&mParentWidget);
 | 
						|
      if (mParentWidget) {
 | 
						|
        // GetParentWidget AddRefs, but mParentWidget is weak
 | 
						|
        mParentWidget->Release();
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    nsView* containerView = FindContainerView();
 | 
						|
 | 
						|
    nsresult rv = CreateDeviceContext(containerView);
 | 
						|
    NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
    // Create presentation context
 | 
						|
    NS_ASSERTION(!mPresContext,
 | 
						|
                 "Shouldn't have a prescontext if we have no shell!");
 | 
						|
    mPresContext = CreatePresContext(mDocument, nsPresContext::eContext_Galley,
 | 
						|
                                     containerView);
 | 
						|
    NS_ENSURE_TRUE(mPresContext, NS_ERROR_OUT_OF_MEMORY);
 | 
						|
 | 
						|
    rv = mPresContext->Init(mDeviceContext);
 | 
						|
    if (NS_FAILED(rv)) {
 | 
						|
      mPresContext = nullptr;
 | 
						|
      return rv;
 | 
						|
    }
 | 
						|
 | 
						|
    rv = MakeWindow(nsSize(mPresContext->DevPixelsToAppUnits(mBounds.width),
 | 
						|
                           mPresContext->DevPixelsToAppUnits(mBounds.height)),
 | 
						|
                    containerView);
 | 
						|
    if (NS_FAILED(rv)) return rv;
 | 
						|
 | 
						|
    if (mPresContext) {
 | 
						|
      Hide();
 | 
						|
 | 
						|
      rv = InitPresentationStuff(mDocument->MayStartLayout());
 | 
						|
    }
 | 
						|
 | 
						|
    // If we get here the document load has already started and the
 | 
						|
    // window is shown because some JS on the page caused it to be
 | 
						|
    // shown...
 | 
						|
 | 
						|
    if (mPresShell) {
 | 
						|
      RefPtr<PresShell> presShell = mPresShell;  // bug 378682
 | 
						|
      presShell->UnsuppressPainting();
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // Notify observers that a new page has been shown. This will get run
 | 
						|
  // from the event loop after we actually draw the page.
 | 
						|
  RefPtr<nsDocumentShownDispatcher> event =
 | 
						|
      new nsDocumentShownDispatcher(document);
 | 
						|
  document->Dispatch(TaskCategory::Other, event.forget());
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::Hide() {
 | 
						|
  if (!mAttachedToParent && mWindow) {
 | 
						|
    mWindow->Show(false);
 | 
						|
  }
 | 
						|
 | 
						|
  if (!mPresShell) return NS_OK;
 | 
						|
 | 
						|
  NS_ASSERTION(mPresContext, "Can't have a presshell and no prescontext!");
 | 
						|
 | 
						|
  // Avoid leaking the old viewer.
 | 
						|
  if (mPreviousViewer) {
 | 
						|
    mPreviousViewer->Destroy();
 | 
						|
    mPreviousViewer = nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  if (mIsSticky) {
 | 
						|
    // This window is sticky, that means that it might be shown again
 | 
						|
    // and we don't want the presshell n' all that to be thrown away
 | 
						|
    // just because the window is hidden.
 | 
						|
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<nsIDocShell> docShell(mContainer);
 | 
						|
  if (docShell) {
 | 
						|
#ifdef DEBUG
 | 
						|
    nsCOMPtr<nsIContentViewer> currentViewer;
 | 
						|
    docShell->GetContentViewer(getter_AddRefs(currentViewer));
 | 
						|
    MOZ_ASSERT(currentViewer == this);
 | 
						|
#endif
 | 
						|
    nsCOMPtr<nsILayoutHistoryState> layoutState;
 | 
						|
    mPresShell->CaptureHistoryState(getter_AddRefs(layoutState));
 | 
						|
  }
 | 
						|
 | 
						|
  // Do not run ScriptRunners queued by DestroyPresShell() in the intermediate
 | 
						|
  // state before we're done destroying PresShell, PresContext, ViewManager,
 | 
						|
  // etc.
 | 
						|
  nsAutoScriptBlocker scriptBlocker;
 | 
						|
 | 
						|
  DestroyPresShell();
 | 
						|
 | 
						|
  DestroyPresContext();
 | 
						|
 | 
						|
  mViewManager = nullptr;
 | 
						|
  mWindow = nullptr;
 | 
						|
  mDeviceContext = nullptr;
 | 
						|
  mParentWidget = nullptr;
 | 
						|
 | 
						|
  nsCOMPtr<nsIBaseWindow> base_win(mContainer);
 | 
						|
 | 
						|
  if (base_win && !mAttachedToParent) {
 | 
						|
    base_win->SetParentWidget(nullptr);
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::GetSticky(bool* aSticky) {
 | 
						|
  *aSticky = mIsSticky;
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::SetSticky(bool aSticky) {
 | 
						|
  mIsSticky = aSticky;
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::ClearHistoryEntry() {
 | 
						|
  if (mDocument) {
 | 
						|
    nsJSContext::PokeGC(JS::GCReason::PAGE_HIDE,
 | 
						|
                        mDocument->GetWrapperPreserveColor(),
 | 
						|
                        TimeDuration::FromMilliseconds(
 | 
						|
                            StaticPrefs::javascript_options_gc_delay() * 2));
 | 
						|
  }
 | 
						|
 | 
						|
  mSHEntry = nullptr;
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
//-------------------------------------------------------
 | 
						|
 | 
						|
nsresult nsDocumentViewer::MakeWindow(const nsSize& aSize,
 | 
						|
                                      nsView* aContainerView) {
 | 
						|
  if (GetIsPrintPreview()) {
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  bool shouldAttach = ShouldAttachToTopLevel();
 | 
						|
 | 
						|
  if (shouldAttach) {
 | 
						|
    // If the old view is already attached to our parent, detach
 | 
						|
    DetachFromTopLevelWidget();
 | 
						|
  }
 | 
						|
 | 
						|
  mViewManager = new nsViewManager();
 | 
						|
 | 
						|
  nsDeviceContext* dx = mPresContext->DeviceContext();
 | 
						|
 | 
						|
  nsresult rv = mViewManager->Init(dx);
 | 
						|
  if (NS_FAILED(rv)) return rv;
 | 
						|
 | 
						|
  // The root view is always at 0,0.
 | 
						|
  nsRect tbounds(nsPoint(0, 0), aSize);
 | 
						|
  // Create a view
 | 
						|
  nsView* view = mViewManager->CreateView(tbounds, aContainerView);
 | 
						|
  if (!view) return NS_ERROR_OUT_OF_MEMORY;
 | 
						|
 | 
						|
  // Create a widget if we were given a parent widget or don't have a
 | 
						|
  // container view that we can hook up to without a widget.
 | 
						|
  // Don't create widgets for ResourceDocs (external resources & svg images),
 | 
						|
  // because when they're displayed, they're painted into *another* document's
 | 
						|
  // widget.
 | 
						|
  if (!mDocument->IsResourceDoc() && (mParentWidget || !aContainerView)) {
 | 
						|
    // pass in a native widget to be the parent widget ONLY if the view
 | 
						|
    // hierarchy will stand alone. otherwise the view will find its own parent
 | 
						|
    // widget and "do the right thing" to establish a parent/child widget
 | 
						|
    // relationship
 | 
						|
    widget::InitData initData;
 | 
						|
    widget::InitData* initDataPtr;
 | 
						|
    if (!mParentWidget) {
 | 
						|
      initDataPtr = &initData;
 | 
						|
      initData.mWindowType = widget::WindowType::Invisible;
 | 
						|
    } else {
 | 
						|
      initDataPtr = nullptr;
 | 
						|
    }
 | 
						|
 | 
						|
    if (shouldAttach) {
 | 
						|
      // Reuse the top level parent widget.
 | 
						|
      rv = view->AttachToTopLevelWidget(mParentWidget);
 | 
						|
      mAttachedToParent = true;
 | 
						|
    } else if (!aContainerView && mParentWidget) {
 | 
						|
      rv = view->CreateWidgetForParent(mParentWidget, initDataPtr, true, false);
 | 
						|
    } else {
 | 
						|
      rv = view->CreateWidget(initDataPtr, true, false);
 | 
						|
    }
 | 
						|
    if (NS_FAILED(rv)) return rv;
 | 
						|
  }
 | 
						|
 | 
						|
  // Setup hierarchical relationship in view manager
 | 
						|
  mViewManager->SetRootView(view);
 | 
						|
 | 
						|
  mWindow = view->GetWidget();
 | 
						|
 | 
						|
  // This SetFocus is necessary so the Arrow Key and Page Key events
 | 
						|
  // go to the scrolled view as soon as the Window is created instead of going
 | 
						|
  // to the browser window (this enables keyboard scrolling of the document)
 | 
						|
  // mWindow->SetFocus();
 | 
						|
 | 
						|
  return rv;
 | 
						|
}
 | 
						|
 | 
						|
void nsDocumentViewer::DetachFromTopLevelWidget() {
 | 
						|
  if (mViewManager) {
 | 
						|
    nsView* oldView = mViewManager->GetRootView();
 | 
						|
    if (oldView && oldView->IsAttachedToTopLevel()) {
 | 
						|
      oldView->DetachFromTopLevelWidget();
 | 
						|
    }
 | 
						|
  }
 | 
						|
  mAttachedToParent = false;
 | 
						|
}
 | 
						|
 | 
						|
nsView* nsDocumentViewer::FindContainerView() {
 | 
						|
  if (!mContainer) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<nsIDocShell> docShell(mContainer);
 | 
						|
  nsCOMPtr<nsPIDOMWindowOuter> pwin(docShell->GetWindow());
 | 
						|
  if (!pwin) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<Element> containerElement = pwin->GetFrameElementInternal();
 | 
						|
  if (!containerElement) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  nsIFrame* subdocFrame = containerElement->GetPrimaryFrame();
 | 
						|
  if (!subdocFrame) {
 | 
						|
    // XXX Silenced by default in bug 1175289
 | 
						|
    LAYOUT_WARNING("Subdocument container has no frame");
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  // Check subdocFrame just to be safe. If this somehow fails we treat that as
 | 
						|
  // display:none, the document is not displayed.
 | 
						|
  if (!subdocFrame->IsSubDocumentFrame()) {
 | 
						|
    NS_WARNING_ASSERTION(subdocFrame->Type() == LayoutFrameType::None,
 | 
						|
                         "Subdocument container has non-subdocument frame");
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  NS_ASSERTION(subdocFrame->GetView(), "Subdoc frames must have views");
 | 
						|
  return static_cast<nsSubDocumentFrame*>(subdocFrame)->EnsureInnerView();
 | 
						|
}
 | 
						|
 | 
						|
nsresult nsDocumentViewer::CreateDeviceContext(nsView* aContainerView) {
 | 
						|
  MOZ_ASSERT(!mPresShell && !mWindow,
 | 
						|
             "This will screw up our existing presentation");
 | 
						|
  MOZ_ASSERT(mDocument, "Gotta have a document here");
 | 
						|
 | 
						|
  Document* doc = mDocument->GetDisplayDocument();
 | 
						|
  if (doc) {
 | 
						|
    NS_ASSERTION(!aContainerView,
 | 
						|
                 "External resource document embedded somewhere?");
 | 
						|
    // We want to use our display document's device context if possible
 | 
						|
    nsPresContext* ctx = doc->GetPresContext();
 | 
						|
    if (ctx) {
 | 
						|
      mDeviceContext = ctx->DeviceContext();
 | 
						|
      return NS_OK;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // Create a device context even if we already have one, since our widget
 | 
						|
  // might have changed.
 | 
						|
  nsIWidget* widget = nullptr;
 | 
						|
  if (aContainerView) {
 | 
						|
    widget = aContainerView->GetNearestWidget(nullptr);
 | 
						|
  }
 | 
						|
  if (!widget) {
 | 
						|
    widget = mParentWidget;
 | 
						|
  }
 | 
						|
  if (widget) {
 | 
						|
    widget = widget->GetTopLevelWidget();
 | 
						|
  }
 | 
						|
 | 
						|
  mDeviceContext = new nsDeviceContext();
 | 
						|
  mDeviceContext->Init(widget);
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
// Return the selection for the document. Note that text fields have their
 | 
						|
// own selection, which cannot be accessed with this method.
 | 
						|
mozilla::dom::Selection* nsDocumentViewer::GetDocumentSelection() {
 | 
						|
  if (!mPresShell) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  return mPresShell->GetCurrentSelection(SelectionType::eNormal);
 | 
						|
}
 | 
						|
 | 
						|
/* ============================================================================
 | 
						|
 * nsIContentViewerEdit
 | 
						|
 * ============================================================================
 | 
						|
 */
 | 
						|
 | 
						|
MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP nsDocumentViewer::ClearSelection() {
 | 
						|
  // use nsCopySupport::GetSelectionForCopy() ?
 | 
						|
  RefPtr<mozilla::dom::Selection> selection = GetDocumentSelection();
 | 
						|
  if (!selection) {
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
 | 
						|
  ErrorResult rv;
 | 
						|
  selection->CollapseToStart(rv);
 | 
						|
  return rv.StealNSResult();
 | 
						|
}
 | 
						|
 | 
						|
MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP nsDocumentViewer::SelectAll() {
 | 
						|
  // XXX this is a temporary implementation copied from nsWebShell
 | 
						|
  // for now. I think Document and friends should have some helper
 | 
						|
  // functions to make this easier.
 | 
						|
 | 
						|
  // use nsCopySupport::GetSelectionForCopy() ?
 | 
						|
  RefPtr<mozilla::dom::Selection> selection = GetDocumentSelection();
 | 
						|
  if (!selection) {
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!mDocument) {
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<nsINode> bodyNode;
 | 
						|
  if (mDocument->IsHTMLOrXHTML()) {
 | 
						|
    // XXXbz why not just do GetBody() for all documents, then GetRootElement()
 | 
						|
    // if GetBody() is null?
 | 
						|
    bodyNode = mDocument->GetBody();
 | 
						|
  } else {
 | 
						|
    bodyNode = mDocument->GetRootElement();
 | 
						|
  }
 | 
						|
  if (!bodyNode) return NS_ERROR_FAILURE;
 | 
						|
 | 
						|
  ErrorResult err;
 | 
						|
  selection->RemoveAllRanges(err);
 | 
						|
  if (err.Failed()) {
 | 
						|
    return err.StealNSResult();
 | 
						|
  }
 | 
						|
 | 
						|
  mozilla::dom::Selection::AutoUserInitiated userSelection(selection);
 | 
						|
  selection->SelectAllChildren(*bodyNode, err);
 | 
						|
  return err.StealNSResult();
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP nsDocumentViewer::CopySelection() {
 | 
						|
  RefPtr<PresShell> presShell = mPresShell;
 | 
						|
  nsCopySupport::FireClipboardEvent(eCopy, nsIClipboard::kGlobalClipboard,
 | 
						|
                                    presShell, nullptr);
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP nsDocumentViewer::CopyLinkLocation() {
 | 
						|
  NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_INITIALIZED);
 | 
						|
  nsCOMPtr<nsINode> node = GetPopupLinkNode();
 | 
						|
  // make noise if we're not in a link
 | 
						|
  NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
 | 
						|
 | 
						|
  nsCOMPtr<dom::Element> elm(do_QueryInterface(node));
 | 
						|
  NS_ENSURE_TRUE(elm, NS_ERROR_FAILURE);
 | 
						|
 | 
						|
  nsAutoString locationText;
 | 
						|
  nsContentUtils::GetLinkLocation(elm, locationText);
 | 
						|
  if (locationText.IsEmpty()) return NS_ERROR_FAILURE;
 | 
						|
 | 
						|
  nsresult rv = NS_OK;
 | 
						|
  nsCOMPtr<nsIClipboardHelper> clipboard(
 | 
						|
      do_GetService("@mozilla.org/widget/clipboardhelper;1", &rv));
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
  // copy the href onto the clipboard
 | 
						|
  return clipboard->CopyString(locationText);
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP nsDocumentViewer::CopyImage(int32_t aCopyFlags) {
 | 
						|
  NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_INITIALIZED);
 | 
						|
  nsCOMPtr<nsIImageLoadingContent> node = GetPopupImageNode();
 | 
						|
  // make noise if we're not in an image
 | 
						|
  NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
 | 
						|
 | 
						|
  nsCOMPtr<nsILoadContext> loadContext(mContainer);
 | 
						|
  return nsCopySupport::ImageCopy(node, loadContext, aCopyFlags);
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP nsDocumentViewer::GetCopyable(bool* aCopyable) {
 | 
						|
  NS_ENSURE_ARG_POINTER(aCopyable);
 | 
						|
  *aCopyable = nsCopySupport::CanCopy(mDocument);
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP nsDocumentViewer::GetContents(const char* mimeType,
 | 
						|
                                            bool selectionOnly,
 | 
						|
                                            nsAString& aOutValue) {
 | 
						|
  aOutValue.Truncate();
 | 
						|
 | 
						|
  NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_INITIALIZED);
 | 
						|
  NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_INITIALIZED);
 | 
						|
 | 
						|
  // Now we have the selection.  Make sure it's nonzero:
 | 
						|
  RefPtr<Selection> sel;
 | 
						|
  if (selectionOnly) {
 | 
						|
    sel = nsCopySupport::GetSelectionForCopy(mDocument);
 | 
						|
    NS_ENSURE_TRUE(sel, NS_ERROR_FAILURE);
 | 
						|
 | 
						|
    if (sel->IsCollapsed()) {
 | 
						|
      return NS_OK;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // call the copy code
 | 
						|
  return nsCopySupport::GetContents(nsDependentCString(mimeType), 0, sel,
 | 
						|
                                    mDocument, aOutValue);
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP nsDocumentViewer::GetCanGetContents(bool* aCanGetContents) {
 | 
						|
  NS_ENSURE_ARG_POINTER(aCanGetContents);
 | 
						|
  *aCanGetContents = false;
 | 
						|
  NS_ENSURE_STATE(mDocument);
 | 
						|
  *aCanGetContents = nsCopySupport::CanCopy(mDocument);
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP nsDocumentViewer::SetCommandNode(nsINode* aNode) {
 | 
						|
  Document* document = GetDocument();
 | 
						|
  NS_ENSURE_STATE(document);
 | 
						|
 | 
						|
  nsCOMPtr<nsPIDOMWindowOuter> window(document->GetWindow());
 | 
						|
  NS_ENSURE_TRUE(window, NS_ERROR_NOT_AVAILABLE);
 | 
						|
 | 
						|
  nsCOMPtr<nsPIWindowRoot> root = window->GetTopWindowRoot();
 | 
						|
  NS_ENSURE_STATE(root);
 | 
						|
 | 
						|
  root->SetPopupNode(aNode);
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::GetDeviceFullZoomForTest(float* aDeviceFullZoom) {
 | 
						|
  NS_ENSURE_ARG_POINTER(aDeviceFullZoom);
 | 
						|
  nsPresContext* pc = GetPresContext();
 | 
						|
  *aDeviceFullZoom = pc ? pc->GetDeviceFullZoom() : 1.0;
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::SetAuthorStyleDisabled(bool aStyleDisabled) {
 | 
						|
  if (mPresShell) {
 | 
						|
    mPresShell->SetAuthorStyleDisabled(aStyleDisabled);
 | 
						|
  }
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::GetAuthorStyleDisabled(bool* aStyleDisabled) {
 | 
						|
  if (mPresShell) {
 | 
						|
    *aStyleDisabled = mPresShell->GetAuthorStyleDisabled();
 | 
						|
  } else {
 | 
						|
    *aStyleDisabled = false;
 | 
						|
  }
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
/* [noscript,notxpcom] Encoding getHintCharset (); */
 | 
						|
NS_IMETHODIMP_(const Encoding*)
 | 
						|
nsDocumentViewer::GetReloadEncodingAndSource(int32_t* aSource) {
 | 
						|
  *aSource = mReloadEncodingSource;
 | 
						|
  if (kCharsetUninitialized == mReloadEncodingSource) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
  return mReloadEncoding;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP_(void)
 | 
						|
nsDocumentViewer::SetReloadEncodingAndSource(const Encoding* aEncoding,
 | 
						|
                                             int32_t aSource) {
 | 
						|
  MOZ_ASSERT(
 | 
						|
      aSource == kCharsetUninitialized ||
 | 
						|
      (aSource >=
 | 
						|
           kCharsetFromFinalAutoDetectionWouldHaveBeenUTF8InitialWasASCII &&
 | 
						|
       aSource <=
 | 
						|
           kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLDInitialWasASCII) ||
 | 
						|
      aSource == kCharsetFromFinalUserForcedAutoDetection);
 | 
						|
  mReloadEncoding = aEncoding;
 | 
						|
  mReloadEncodingSource = aSource;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP_(void)
 | 
						|
nsDocumentViewer::ForgetReloadEncoding() {
 | 
						|
  mReloadEncoding = nullptr;
 | 
						|
  mReloadEncodingSource = kCharsetUninitialized;
 | 
						|
}
 | 
						|
 | 
						|
MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP nsDocumentViewer::GetContentSize(
 | 
						|
    int32_t aMaxWidth, int32_t aMaxHeight, int32_t aPrefWidth, int32_t* aWidth,
 | 
						|
    int32_t* aHeight) {
 | 
						|
  RefPtr<BrowsingContext> bc = mContainer->GetBrowsingContext();
 | 
						|
  NS_ENSURE_TRUE(bc, NS_ERROR_NOT_AVAILABLE);
 | 
						|
 | 
						|
  // It's only valid to access this from a top frame.  Doesn't work from
 | 
						|
  // sub-frames.
 | 
						|
  NS_ENSURE_TRUE(bc->IsTop(), NS_ERROR_FAILURE);
 | 
						|
 | 
						|
  // Convert max-width/height and pref-width to app units.
 | 
						|
  if (aMaxWidth > 0) {
 | 
						|
    aMaxWidth = CSSPixel::ToAppUnits(aMaxWidth);
 | 
						|
  } else {
 | 
						|
    aMaxWidth = NS_UNCONSTRAINEDSIZE;
 | 
						|
  }
 | 
						|
  if (aMaxHeight > 0) {
 | 
						|
    aMaxHeight = CSSPixel::ToAppUnits(aMaxHeight);
 | 
						|
  } else {
 | 
						|
    aMaxHeight = NS_UNCONSTRAINEDSIZE;
 | 
						|
  }
 | 
						|
  if (aPrefWidth > 0) {
 | 
						|
    aPrefWidth = CSSPixel::ToAppUnits(aPrefWidth);
 | 
						|
  } else {
 | 
						|
    aPrefWidth = 0;
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<PresShell> presShell = GetPresShell();
 | 
						|
  NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
 | 
						|
 | 
						|
  // Flush out all content and style updates. We can't use a resize reflow
 | 
						|
  // because it won't change some sizes that a style change reflow will.
 | 
						|
  mDocument->FlushPendingNotifications(FlushType::Layout);
 | 
						|
 | 
						|
  nsIFrame* root = presShell->GetRootFrame();
 | 
						|
  NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
 | 
						|
 | 
						|
  WritingMode wm = root->GetWritingMode();
 | 
						|
 | 
						|
  nscoord prefISize;
 | 
						|
  {
 | 
						|
    const auto& constraints = presShell->GetWindowSizeConstraints();
 | 
						|
    aMaxHeight = std::min(aMaxHeight, constraints.mMaxSize.height);
 | 
						|
    aMaxWidth = std::min(aMaxWidth, constraints.mMaxSize.width);
 | 
						|
 | 
						|
    UniquePtr<gfxContext> rcx(presShell->CreateReferenceRenderingContext());
 | 
						|
    const nscoord minISize = wm.IsVertical() ? constraints.mMinSize.height
 | 
						|
                                             : constraints.mMinSize.width;
 | 
						|
    const nscoord maxISize = wm.IsVertical() ? aMaxHeight : aMaxWidth;
 | 
						|
    if (aPrefWidth) {
 | 
						|
      prefISize = std::max(root->GetMinISize(rcx.get()), aPrefWidth);
 | 
						|
    } else {
 | 
						|
      prefISize = root->GetPrefISize(rcx.get());
 | 
						|
    }
 | 
						|
    prefISize = std::max(minISize, std::min(prefISize, maxISize));
 | 
						|
  }
 | 
						|
 | 
						|
  // We should never intentionally get here with this sentinel value, but it's
 | 
						|
  // possible that a document with huge sizes might inadvertently have a
 | 
						|
  // prefISize that exactly matches NS_UNCONSTRAINEDSIZE.
 | 
						|
  // Just bail if that happens.
 | 
						|
  NS_ENSURE_TRUE(prefISize != NS_UNCONSTRAINEDSIZE, NS_ERROR_FAILURE);
 | 
						|
 | 
						|
  nscoord height = wm.IsVertical() ? prefISize : aMaxHeight;
 | 
						|
  nscoord width = wm.IsVertical() ? aMaxWidth : prefISize;
 | 
						|
 | 
						|
  presShell->ResizeReflow(width, height, ResizeReflowOptions::BSizeLimit);
 | 
						|
 | 
						|
  RefPtr<nsPresContext> presContext = GetPresContext();
 | 
						|
  NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
 | 
						|
 | 
						|
  // Protect against bogus returns here
 | 
						|
  nsRect shellArea = presContext->GetVisibleArea();
 | 
						|
  NS_ENSURE_TRUE(shellArea.width != NS_UNCONSTRAINEDSIZE &&
 | 
						|
                     shellArea.height != NS_UNCONSTRAINEDSIZE,
 | 
						|
                 NS_ERROR_FAILURE);
 | 
						|
 | 
						|
  // Ceil instead of rounding here, so we can actually guarantee showing all the
 | 
						|
  // content.
 | 
						|
  *aWidth = std::ceil(CSSPixel::FromAppUnits(shellArea.width));
 | 
						|
  *aHeight = std::ceil(CSSPixel::FromAppUnits(shellArea.height));
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMPL_ISUPPORTS(nsDocViewerSelectionListener, nsISelectionListener)
 | 
						|
 | 
						|
/*
 | 
						|
 * GetPopupNode, GetPopupLinkNode and GetPopupImageNode are helpers
 | 
						|
 * for the cmd_copyLink / cmd_copyImageLocation / cmd_copyImageContents family
 | 
						|
 * of commands. The focus controller stores the popup node, these retrieve
 | 
						|
 * them and munge appropriately. Note that we have to store the popup node
 | 
						|
 * rather than retrieving it from EventStateManager::GetFocusedContent because
 | 
						|
 * not all content (images included) can receive focus.
 | 
						|
 */
 | 
						|
 | 
						|
already_AddRefed<nsINode> nsDocumentViewer::GetPopupNode() {
 | 
						|
  // get the document
 | 
						|
  Document* document = GetDocument();
 | 
						|
  NS_ENSURE_TRUE(document, nullptr);
 | 
						|
 | 
						|
  // get the private dom window
 | 
						|
  nsCOMPtr<nsPIDOMWindowOuter> window(document->GetWindow());
 | 
						|
  NS_ENSURE_TRUE(window, nullptr);
 | 
						|
  if (window) {
 | 
						|
    nsCOMPtr<nsPIWindowRoot> root = window->GetTopWindowRoot();
 | 
						|
    NS_ENSURE_TRUE(root, nullptr);
 | 
						|
 | 
						|
    // get the popup node
 | 
						|
    nsCOMPtr<nsINode> node = root->GetPopupNode();
 | 
						|
    if (!node) {
 | 
						|
      nsPIDOMWindowOuter* rootWindow = root->GetWindow();
 | 
						|
      if (rootWindow) {
 | 
						|
        nsCOMPtr<Document> rootDoc = rootWindow->GetExtantDoc();
 | 
						|
        if (rootDoc) {
 | 
						|
          nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
 | 
						|
          if (pm) {
 | 
						|
            node = pm->GetLastTriggerPopupNode(rootDoc);
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return node.forget();
 | 
						|
  }
 | 
						|
 | 
						|
  return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
// GetPopupLinkNode: return popup link node or fail
 | 
						|
already_AddRefed<nsINode> nsDocumentViewer::GetPopupLinkNode() {
 | 
						|
  // find popup node
 | 
						|
  nsCOMPtr<nsINode> node = GetPopupNode();
 | 
						|
 | 
						|
  // find out if we have a link in our ancestry
 | 
						|
  while (node) {
 | 
						|
    if (const auto* element = Element::FromNode(*node)) {
 | 
						|
      if (element->IsLink()) {
 | 
						|
        return node.forget();
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    // get our parent and keep trying...
 | 
						|
    node = node->GetParentNode();
 | 
						|
  }
 | 
						|
 | 
						|
  // if we have no node, fail
 | 
						|
  return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
// GetPopupLinkNode: return popup image node or fail
 | 
						|
already_AddRefed<nsIImageLoadingContent> nsDocumentViewer::GetPopupImageNode() {
 | 
						|
  // find popup node
 | 
						|
  nsCOMPtr<nsINode> node = GetPopupNode();
 | 
						|
  nsCOMPtr<nsIImageLoadingContent> img = do_QueryInterface(node);
 | 
						|
  return img.forget();
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * XXX dr
 | 
						|
 * ------
 | 
						|
 * These two functions -- GetInLink and GetInImage -- are kind of annoying
 | 
						|
 * in that they only get called from the controller (in
 | 
						|
 * nsDOMWindowController::IsCommandEnabled). The actual construction of the
 | 
						|
 * context menus in communicator (nsContextMenu.js) has its own, redundant
 | 
						|
 * tests. No big deal, but good to keep in mind if we ever clean context
 | 
						|
 * menus.
 | 
						|
 */
 | 
						|
 | 
						|
NS_IMETHODIMP nsDocumentViewer::GetInLink(bool* aInLink) {
 | 
						|
#ifdef DEBUG_dr
 | 
						|
  printf("dr :: nsDocumentViewer::GetInLink\n");
 | 
						|
#endif
 | 
						|
 | 
						|
  NS_ENSURE_ARG_POINTER(aInLink);
 | 
						|
 | 
						|
  // we're not in a link unless i say so
 | 
						|
  *aInLink = false;
 | 
						|
 | 
						|
  // get the popup link
 | 
						|
  nsCOMPtr<nsINode> node = GetPopupLinkNode();
 | 
						|
  if (!node) {
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
 | 
						|
  // if we made it here, we're in a link
 | 
						|
  *aInLink = true;
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP nsDocumentViewer::GetInImage(bool* aInImage) {
 | 
						|
#ifdef DEBUG_dr
 | 
						|
  printf("dr :: nsDocumentViewer::GetInImage\n");
 | 
						|
#endif
 | 
						|
 | 
						|
  NS_ENSURE_ARG_POINTER(aInImage);
 | 
						|
 | 
						|
  // we're not in an image unless i say so
 | 
						|
  *aInImage = false;
 | 
						|
 | 
						|
  // get the popup image
 | 
						|
  nsCOMPtr<nsIImageLoadingContent> node = GetPopupImageNode();
 | 
						|
  if (!node) {
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
 | 
						|
  // Make sure there is a URI assigned. This allows <input type="image"> to
 | 
						|
  // be an image but rejects other <input> types. This matches what
 | 
						|
  // nsContextMenu.js does.
 | 
						|
  nsCOMPtr<nsIURI> uri;
 | 
						|
  node->GetCurrentURI(getter_AddRefs(uri));
 | 
						|
  if (uri) {
 | 
						|
    // if we made it here, we're in an image
 | 
						|
    *aInImage = true;
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP nsDocViewerSelectionListener::NotifySelectionChanged(
 | 
						|
    Document*, Selection*, int16_t aReason, int32_t aAmount) {
 | 
						|
  if (!mDocViewer) {
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  // get the selection state
 | 
						|
  RefPtr<mozilla::dom::Selection> selection =
 | 
						|
      mDocViewer->GetDocumentSelection();
 | 
						|
  if (!selection) {
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
 | 
						|
  Document* theDoc = mDocViewer->GetDocument();
 | 
						|
  if (!theDoc) return NS_ERROR_FAILURE;
 | 
						|
 | 
						|
  nsCOMPtr<nsPIDOMWindowOuter> domWindow = theDoc->GetWindow();
 | 
						|
  if (!domWindow) return NS_ERROR_FAILURE;
 | 
						|
 | 
						|
  bool selectionCollapsed = selection->IsCollapsed();
 | 
						|
  // We only call UpdateCommands when the selection changes from collapsed to
 | 
						|
  // non-collapsed or vice versa, however we skip the initializing collapse. We
 | 
						|
  // might need another update string for simple selection changes, but that
 | 
						|
  // would be expenseive.
 | 
						|
  if (mSelectionWasCollapsed != selectionCollapsed) {
 | 
						|
    domWindow->UpdateCommands(u"select"_ns, selection, aReason);
 | 
						|
    mSelectionWasCollapsed = selectionCollapsed;
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
// nsDocViewerFocusListener
 | 
						|
NS_IMPL_ISUPPORTS(nsDocViewerFocusListener, nsIDOMEventListener)
 | 
						|
 | 
						|
nsresult nsDocViewerFocusListener::HandleEvent(Event* aEvent) {
 | 
						|
  NS_ENSURE_STATE(mDocViewer);
 | 
						|
 | 
						|
  RefPtr<PresShell> presShell = mDocViewer->GetPresShell();
 | 
						|
  NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
 | 
						|
 | 
						|
  RefPtr<nsFrameSelection> selection =
 | 
						|
      presShell->GetLastFocusedFrameSelection();
 | 
						|
  NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
 | 
						|
  auto selectionStatus = selection->GetDisplaySelection();
 | 
						|
  nsAutoString eventType;
 | 
						|
  aEvent->GetType(eventType);
 | 
						|
  if (eventType.EqualsLiteral("focus")) {
 | 
						|
    // If selection was disabled, re-enable it.
 | 
						|
    if (selectionStatus == nsISelectionController::SELECTION_DISABLED ||
 | 
						|
        selectionStatus == nsISelectionController::SELECTION_HIDDEN) {
 | 
						|
      selection->SetDisplaySelection(nsISelectionController::SELECTION_ON);
 | 
						|
      selection->RepaintSelection(SelectionType::eNormal);
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    MOZ_ASSERT(eventType.EqualsLiteral("blur"), "Unexpected event type");
 | 
						|
    // If selection was on, disable it.
 | 
						|
    if (selectionStatus == nsISelectionController::SELECTION_ON ||
 | 
						|
        selectionStatus == nsISelectionController::SELECTION_ATTENTION) {
 | 
						|
      selection->SetDisplaySelection(
 | 
						|
          nsISelectionController::SELECTION_DISABLED);
 | 
						|
      selection->RepaintSelection(SelectionType::eNormal);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
/** ---------------------------------------------------
 | 
						|
 *  From nsIWebBrowserPrint
 | 
						|
 */
 | 
						|
 | 
						|
#ifdef NS_PRINTING
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::Print(nsIPrintSettings* aPrintSettings,
 | 
						|
                        RemotePrintJobChild* aRemotePrintJob,
 | 
						|
                        nsIWebProgressListener* aWebProgressListener) {
 | 
						|
  if (NS_WARN_IF(!mContainer)) {
 | 
						|
    PR_PL(("Container was destroyed yet we are still trying to use it!"));
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
 | 
						|
  if (NS_WARN_IF(!mDocument) || NS_WARN_IF(!mDeviceContext)) {
 | 
						|
    PR_PL(("Can't Print without a document and a device context"));
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
 | 
						|
  if (NS_WARN_IF(mPrintJob && mPrintJob->GetIsPrinting())) {
 | 
						|
    // If we are printing another URL, then exit.
 | 
						|
    // The reason we check here is because this method can be called while
 | 
						|
    // another is still in here (the printing dialog is a good example).  the
 | 
						|
    // only time we can print more than one job at a time is the regression
 | 
						|
    // tests.
 | 
						|
    nsresult rv = NS_ERROR_NOT_AVAILABLE;
 | 
						|
    RefPtr<nsPrintJob>(mPrintJob)->FirePrintingErrorEvent(rv);
 | 
						|
    return rv;
 | 
						|
  }
 | 
						|
 | 
						|
  OnDonePrinting();
 | 
						|
 | 
						|
  // Note: mContainer and mDocument are known to be non-null via null-checks
 | 
						|
  // earlier in this function.
 | 
						|
  // TODO(dholbert) Do we need to bother with this stack-owned local RefPtr?
 | 
						|
  // (Is there an edge case where it's needed to keep the nsPrintJob alive?)
 | 
						|
  RefPtr<nsPrintJob> printJob =
 | 
						|
      new nsPrintJob(*this, *mContainer, *mDocument,
 | 
						|
                     float(AppUnitsPerCSSInch()) /
 | 
						|
                         float(mDeviceContext->AppUnitsPerDevPixel()));
 | 
						|
  mPrintJob = printJob;
 | 
						|
 | 
						|
  nsresult rv = printJob->Print(*mDocument, aPrintSettings, aRemotePrintJob,
 | 
						|
                                aWebProgressListener);
 | 
						|
  if (NS_WARN_IF(NS_FAILED(rv))) {
 | 
						|
    OnDonePrinting();
 | 
						|
  }
 | 
						|
  return rv;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::PrintPreview(nsIPrintSettings* aPrintSettings,
 | 
						|
                               nsIWebProgressListener* aWebProgressListener,
 | 
						|
                               PrintPreviewResolver&& aCallback) {
 | 
						|
#  ifdef NS_PRINT_PREVIEW
 | 
						|
  RefPtr<Document> doc = mDocument.get();
 | 
						|
  NS_ENSURE_STATE(doc);
 | 
						|
 | 
						|
  if (NS_WARN_IF(GetIsPrinting())) {
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<nsIDocShell> docShell(mContainer);
 | 
						|
  if (NS_WARN_IF(!docShell) || NS_WARN_IF(!mDeviceContext)) {
 | 
						|
    PR_PL(("Can't Print Preview without device context and docshell"));
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
 | 
						|
  NS_ENSURE_STATE(!GetIsPrinting());
 | 
						|
  // beforeprint event may have caused ContentViewer to be shutdown.
 | 
						|
  NS_ENSURE_STATE(mContainer);
 | 
						|
  NS_ENSURE_STATE(mDeviceContext);
 | 
						|
 | 
						|
  OnDonePrinting();
 | 
						|
 | 
						|
  // Note: mContainer and doc are known to be non-null via null-checks earlier
 | 
						|
  // in this function.
 | 
						|
  // TODO(dholbert) Do we need to bother with this stack-owned local RefPtr?
 | 
						|
  // (Is there an edge case where it's needed to keep the nsPrintJob alive?)
 | 
						|
  RefPtr<nsPrintJob> printJob =
 | 
						|
      new nsPrintJob(*this, *mContainer, *doc,
 | 
						|
                     float(AppUnitsPerCSSInch()) /
 | 
						|
                         float(mDeviceContext->AppUnitsPerDevPixel()));
 | 
						|
  mPrintJob = printJob;
 | 
						|
 | 
						|
  nsresult rv = printJob->PrintPreview(
 | 
						|
      *doc, aPrintSettings, aWebProgressListener, std::move(aCallback));
 | 
						|
  if (NS_WARN_IF(NS_FAILED(rv))) {
 | 
						|
    OnDonePrinting();
 | 
						|
  }
 | 
						|
  return rv;
 | 
						|
#  else
 | 
						|
  return NS_ERROR_FAILURE;
 | 
						|
#  endif  // NS_PRINT_PREVIEW
 | 
						|
}
 | 
						|
 | 
						|
static const nsIFrame* GetTargetPageFrame(int32_t aTargetPageNum,
 | 
						|
                                          nsPageSequenceFrame* aSequenceFrame) {
 | 
						|
  MOZ_ASSERT(aTargetPageNum > 0 &&
 | 
						|
             aTargetPageNum <=
 | 
						|
                 aSequenceFrame->PrincipalChildList().GetLength());
 | 
						|
  return aSequenceFrame->PrincipalChildList().FrameAt(aTargetPageNum - 1);
 | 
						|
}
 | 
						|
 | 
						|
// Calculate the scroll position where the center of |aFrame| is positioned at
 | 
						|
// the center of |aScrollable|'s scroll port for the print preview.
 | 
						|
// So what we do for that is;
 | 
						|
// 1) Calculate the position of the center of |aFrame| in the print preview
 | 
						|
//    coordinates.
 | 
						|
// 2) Reduce the half height of the scroll port from the result of 1.
 | 
						|
static nscoord ScrollPositionForFrame(const nsIFrame* aFrame,
 | 
						|
                                      nsIScrollableFrame* aScrollable,
 | 
						|
                                      float aPreviewScale) {
 | 
						|
  // Note that even if the computed scroll position is out of the range of
 | 
						|
  // the scroll port, it gets clamped in nsIScrollableFrame::ScrollTo.
 | 
						|
  return nscoord(aPreviewScale * aFrame->GetRect().Center().y -
 | 
						|
                 float(aScrollable->GetScrollPortRect().height) / 2.0f);
 | 
						|
}
 | 
						|
 | 
						|
//----------------------------------------------------------------------
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::PrintPreviewScrollToPage(int16_t aType, int32_t aPageNum) {
 | 
						|
  if (!GetIsPrintPreview() || mPrintJob->GetIsCreatingPrintPreview())
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
 | 
						|
  nsIScrollableFrame* sf = mPresShell->GetRootScrollFrameAsScrollable();
 | 
						|
  if (!sf) return NS_OK;
 | 
						|
 | 
						|
  auto [seqFrame, sheetCount] = mPrintJob->GetSeqFrameAndCountSheets();
 | 
						|
  Unused << sheetCount;
 | 
						|
  if (!seqFrame) {
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
 | 
						|
  float previewScale = seqFrame->GetPrintPreviewScale();
 | 
						|
 | 
						|
  nsPoint dest = sf->GetScrollPosition();
 | 
						|
 | 
						|
  switch (aType) {
 | 
						|
    case nsIWebBrowserPrint::PRINTPREVIEW_HOME:
 | 
						|
      dest.y = 0;
 | 
						|
      break;
 | 
						|
    case nsIWebBrowserPrint::PRINTPREVIEW_END:
 | 
						|
      dest.y = sf->GetScrollRange().YMost();
 | 
						|
      break;
 | 
						|
    case nsIWebBrowserPrint::PRINTPREVIEW_PREV_PAGE:
 | 
						|
    case nsIWebBrowserPrint::PRINTPREVIEW_NEXT_PAGE: {
 | 
						|
      auto [currentFrame, currentSheetNumber] = GetCurrentSheetFrameAndNumber();
 | 
						|
      Unused << currentSheetNumber;
 | 
						|
      if (!currentFrame) {
 | 
						|
        return NS_OK;
 | 
						|
      }
 | 
						|
 | 
						|
      const nsIFrame* targetFrame = nullptr;
 | 
						|
      if (aType == nsIWebBrowserPrint::PRINTPREVIEW_PREV_PAGE) {
 | 
						|
        targetFrame = currentFrame->GetPrevInFlow();
 | 
						|
      } else {
 | 
						|
        targetFrame = currentFrame->GetNextInFlow();
 | 
						|
      }
 | 
						|
      if (!targetFrame) {
 | 
						|
        return NS_OK;
 | 
						|
      }
 | 
						|
 | 
						|
      dest.y = ScrollPositionForFrame(targetFrame, sf, previewScale);
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case nsIWebBrowserPrint::PRINTPREVIEW_GOTO_PAGENUM: {
 | 
						|
      if (aPageNum <= 0 || aPageNum > sheetCount) {
 | 
						|
        return NS_ERROR_INVALID_ARG;
 | 
						|
      }
 | 
						|
 | 
						|
      const nsIFrame* targetFrame = GetTargetPageFrame(aPageNum, seqFrame);
 | 
						|
      MOZ_ASSERT(targetFrame);
 | 
						|
 | 
						|
      dest.y = ScrollPositionForFrame(targetFrame, sf, previewScale);
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    default:
 | 
						|
      return NS_ERROR_INVALID_ARG;
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  sf->ScrollTo(dest, ScrollMode::Instant);
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
std::tuple<const nsIFrame*, int32_t>
 | 
						|
nsDocumentViewer::GetCurrentSheetFrameAndNumber() const {
 | 
						|
  MOZ_ASSERT(mPrintJob);
 | 
						|
  MOZ_ASSERT(GetIsPrintPreview() && !mPrintJob->GetIsCreatingPrintPreview());
 | 
						|
 | 
						|
  // in PP mPrtPreview->mPrintObject->mSeqFrame is null
 | 
						|
  auto [seqFrame, sheetCount] = mPrintJob->GetSeqFrameAndCountSheets();
 | 
						|
  Unused << sheetCount;
 | 
						|
  if (!seqFrame) {
 | 
						|
    return {nullptr, 0};
 | 
						|
  }
 | 
						|
 | 
						|
  nsIScrollableFrame* sf = mPresShell->GetRootScrollFrameAsScrollable();
 | 
						|
  if (!sf) {
 | 
						|
    // No scrollable contents, returns 1 even if there are multiple sheets.
 | 
						|
    return {seqFrame->PrincipalChildList().FirstChild(), 1};
 | 
						|
  }
 | 
						|
 | 
						|
  nsPoint currentScrollPosition = sf->GetScrollPosition();
 | 
						|
  float halfwayPoint =
 | 
						|
      currentScrollPosition.y + float(sf->GetScrollPortRect().height) / 2.0f;
 | 
						|
  float lastDistanceFromHalfwayPoint = std::numeric_limits<float>::max();
 | 
						|
  int32_t sheetNumber = 0;
 | 
						|
  const nsIFrame* currentSheet = nullptr;
 | 
						|
  float previewScale = seqFrame->GetPrintPreviewScale();
 | 
						|
  for (const nsIFrame* sheetFrame : seqFrame->PrincipalChildList()) {
 | 
						|
    nsRect sheetRect = sheetFrame->GetRect();
 | 
						|
    sheetNumber++;
 | 
						|
    currentSheet = sheetFrame;
 | 
						|
 | 
						|
    float bottomOfSheet = sheetRect.YMost() * previewScale;
 | 
						|
    if (bottomOfSheet < halfwayPoint) {
 | 
						|
      // If the bottom of the sheet is not yet over the halfway point, iterate
 | 
						|
      // the next frame to see if the next frame is over the halfway point and
 | 
						|
      // compare the distance from the halfway point.
 | 
						|
      lastDistanceFromHalfwayPoint = halfwayPoint - bottomOfSheet;
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    float topOfSheet = sheetRect.Y() * previewScale;
 | 
						|
    if (topOfSheet <= halfwayPoint) {
 | 
						|
      // If the top of the sheet is not yet over the halfway point or on the
 | 
						|
      // point, it's the current sheet.
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    // Now the sheet rect is completely over the halfway point, compare the
 | 
						|
    // distances from the halfway point.
 | 
						|
    if ((topOfSheet - halfwayPoint) >= lastDistanceFromHalfwayPoint) {
 | 
						|
      // If the previous sheet distance is less than or equal to the current
 | 
						|
      // sheet distance, choose the previous one as the current.
 | 
						|
      sheetNumber--;
 | 
						|
      MOZ_ASSERT(sheetNumber > 0);
 | 
						|
      currentSheet = currentSheet->GetPrevInFlow();
 | 
						|
      MOZ_ASSERT(currentSheet);
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  }
 | 
						|
 | 
						|
  MOZ_ASSERT(sheetNumber <= sheetCount);
 | 
						|
  return {currentSheet, sheetNumber};
 | 
						|
}
 | 
						|
 | 
						|
// XXXdholbert As noted in nsIWebBrowserPrint.idl, this API (the IDL attr
 | 
						|
// 'printPreviewCurrentPageNumber') is misnamed and needs s/Page/Sheet/.  See
 | 
						|
// bug 1669762.
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::GetPrintPreviewCurrentPageNumber(int32_t* aNumber) {
 | 
						|
  NS_ENSURE_ARG_POINTER(aNumber);
 | 
						|
  NS_ENSURE_TRUE(mPrintJob, NS_ERROR_FAILURE);
 | 
						|
  if (!GetIsPrintPreview() || mPrintJob->GetIsCreatingPrintPreview()) {
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
 | 
						|
  auto [currentFrame, currentSheetNumber] = GetCurrentSheetFrameAndNumber();
 | 
						|
  Unused << currentFrame;
 | 
						|
  if (!currentSheetNumber) {
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
 | 
						|
  *aNumber = currentSheetNumber;
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
// XXX This always returns false for subdocuments
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::GetDoingPrint(bool* aDoingPrint) {
 | 
						|
  NS_ENSURE_ARG_POINTER(aDoingPrint);
 | 
						|
 | 
						|
  // XXX shouldn't this be GetDoingPrint() ?
 | 
						|
  *aDoingPrint = mPrintJob ? mPrintJob->CreatedForPrintPreview() : false;
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
// XXX This always returns false for subdocuments
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::GetDoingPrintPreview(bool* aDoingPrintPreview) {
 | 
						|
  NS_ENSURE_ARG_POINTER(aDoingPrintPreview);
 | 
						|
 | 
						|
  *aDoingPrintPreview = mPrintJob ? mPrintJob->CreatedForPrintPreview() : false;
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::ExitPrintPreview() {
 | 
						|
  NS_ENSURE_TRUE(mPrintJob, NS_ERROR_FAILURE);
 | 
						|
 | 
						|
  if (GetIsPrinting()) {
 | 
						|
    // Block exiting the print preview window if we're in the middle of an
 | 
						|
    // actual print.
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!GetIsPrintPreview()) {
 | 
						|
    NS_ERROR("Wow, we should never get here!");
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
#  ifdef NS_PRINT_PREVIEW
 | 
						|
  mPrintJob->Destroy();
 | 
						|
  mPrintJob = nullptr;
 | 
						|
 | 
						|
  // Since the print preview implementation discards the window that was used
 | 
						|
  // to show the print preview, we skip certain cleanup that we would otherwise
 | 
						|
  // want to do.  Specifically, we do not call `SetIsPrintPreview(false)` to
 | 
						|
  // unblock navigation, we do not call `SetOverrideDPPX` to reset the
 | 
						|
  // devicePixelRatio, and we do not call `Show` to make such changes take
 | 
						|
  // affect.
 | 
						|
#  endif  // NS_PRINT_PREVIEW
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::GetRawNumPages(int32_t* aRawNumPages) {
 | 
						|
  NS_ENSURE_ARG_POINTER(aRawNumPages);
 | 
						|
  NS_ENSURE_TRUE(mPrintJob, NS_ERROR_FAILURE);
 | 
						|
 | 
						|
  *aRawNumPages = mPrintJob->GetRawNumPages();
 | 
						|
  return *aRawNumPages > 0 ? NS_OK : NS_ERROR_FAILURE;
 | 
						|
}
 | 
						|
 | 
						|
// XXXdholbert As noted in nsIWebBrowserPrint.idl, this API (the IDL attr
 | 
						|
// 'printPreviewNumPages') is misnamed and needs s/Page/Sheet/.
 | 
						|
// See bug 1669762.
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::GetPrintPreviewNumPages(int32_t* aPrintPreviewNumPages) {
 | 
						|
  NS_ENSURE_ARG_POINTER(aPrintPreviewNumPages);
 | 
						|
  NS_ENSURE_TRUE(mPrintJob, NS_ERROR_FAILURE);
 | 
						|
  *aPrintPreviewNumPages = mPrintJob->GetPrintPreviewNumSheets();
 | 
						|
  return *aPrintPreviewNumPages > 0 ? NS_OK : NS_ERROR_FAILURE;
 | 
						|
}
 | 
						|
 | 
						|
//----------------------------------------------------------------------------------
 | 
						|
// Printing/Print Preview Helpers
 | 
						|
//----------------------------------------------------------------------------------
 | 
						|
 | 
						|
//----------------------------------------------------------------------------------
 | 
						|
// Walks the document tree and tells each DocShell whether Printing/PP is
 | 
						|
// happening
 | 
						|
#endif  // NS_PRINTING
 | 
						|
 | 
						|
bool nsDocumentViewer::ShouldAttachToTopLevel() {
 | 
						|
  if (!mParentWidget) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!mContainer) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  // We always attach when using puppet widgets
 | 
						|
  if (nsIWidget::UsePuppetWidgets()) {
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) || \
 | 
						|
    defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_UIKIT)
 | 
						|
  if (!mPresContext) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  // On windows, in the parent process we also attach, but just to
 | 
						|
  // chrome items
 | 
						|
  auto winType = mParentWidget->GetWindowType();
 | 
						|
  if ((winType == widget::WindowType::TopLevel ||
 | 
						|
       winType == widget::WindowType::Dialog ||
 | 
						|
       winType == widget::WindowType::Invisible) &&
 | 
						|
      mPresContext->IsChrome()) {
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
//------------------------------------------------------------
 | 
						|
// XXX this always returns false for subdocuments
 | 
						|
bool nsDocumentViewer::GetIsPrinting() const {
 | 
						|
#ifdef NS_PRINTING
 | 
						|
  if (mPrintJob) {
 | 
						|
    return mPrintJob->GetIsPrinting();
 | 
						|
  }
 | 
						|
#endif
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
//------------------------------------------------------------
 | 
						|
// The PrintJob holds the current value
 | 
						|
// this called from inside the DocViewer.
 | 
						|
// XXX it always returns false for subdocuments
 | 
						|
bool nsDocumentViewer::GetIsPrintPreview() const {
 | 
						|
#ifdef NS_PRINTING
 | 
						|
  return mPrintJob && mPrintJob->CreatedForPrintPreview();
 | 
						|
#else
 | 
						|
  return false;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
//------------------------------------------------------------
 | 
						|
// Notification from the PrintJob of the current PP status
 | 
						|
void nsDocumentViewer::SetIsPrintPreview(bool aIsPrintPreview) {
 | 
						|
  // Protect against pres shell destruction running scripts.
 | 
						|
  nsAutoScriptBlocker scriptBlocker;
 | 
						|
 | 
						|
  if (!aIsPrintPreview) {
 | 
						|
    InvalidatePotentialSubDocDisplayItem();
 | 
						|
    if (mPresShell) {
 | 
						|
      DestroyPresShell();
 | 
						|
    }
 | 
						|
    mWindow = nullptr;
 | 
						|
    mViewManager = nullptr;
 | 
						|
    mPresContext = nullptr;
 | 
						|
    mPresShell = nullptr;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
//----------------------------------------------------------------------------------
 | 
						|
// nsIDocumentViewerPrint IFace
 | 
						|
//----------------------------------------------------------------------------------
 | 
						|
 | 
						|
//------------------------------------------------------------
 | 
						|
void nsDocumentViewer::IncrementDestroyBlockedCount() {
 | 
						|
  ++mDestroyBlockedCount;
 | 
						|
}
 | 
						|
 | 
						|
void nsDocumentViewer::DecrementDestroyBlockedCount() {
 | 
						|
  --mDestroyBlockedCount;
 | 
						|
}
 | 
						|
 | 
						|
//------------------------------------------------------------
 | 
						|
// This called ONLY when printing has completed and the DV
 | 
						|
// is being notified that it should get rid of the nsPrintJob.
 | 
						|
//
 | 
						|
// BUT, if we are in Print Preview then we want to ignore the
 | 
						|
// notification (we do not get rid of the nsPrintJob)
 | 
						|
//
 | 
						|
// One small caveat:
 | 
						|
//   This IS called from two places in this module for cleaning
 | 
						|
//   up when an error occurred during the start up printing
 | 
						|
//   and print preview
 | 
						|
//
 | 
						|
void nsDocumentViewer::OnDonePrinting() {
 | 
						|
#if defined(NS_PRINTING) && defined(NS_PRINT_PREVIEW)
 | 
						|
  // If Destroy() has been called during calling nsPrintJob::Print() or
 | 
						|
  // nsPrintJob::PrintPreview(), mPrintJob is already nullptr here.
 | 
						|
  // So, the following clean up does nothing in such case.
 | 
						|
  // (Do we need some of this for that case?)
 | 
						|
  if (mPrintJob) {
 | 
						|
    RefPtr<nsPrintJob> printJob = std::move(mPrintJob);
 | 
						|
    if (GetIsPrintPreview()) {
 | 
						|
      printJob->DestroyPrintingData();
 | 
						|
    } else {
 | 
						|
      printJob->Destroy();
 | 
						|
    }
 | 
						|
 | 
						|
    // We are done printing, now clean up.
 | 
						|
    //
 | 
						|
    // For non-print-preview jobs, we are actually responsible for cleaning up
 | 
						|
    // our whole <browser> or window (see the OPEN_PRINT_BROWSER code), so gotta
 | 
						|
    // run window.close(), which will take care of this.
 | 
						|
    //
 | 
						|
    // For print preview jobs the front-end code is responsible for cleaning the
 | 
						|
    // UI.
 | 
						|
    if (!printJob->CreatedForPrintPreview()) {
 | 
						|
      if (mContainer) {
 | 
						|
        if (nsCOMPtr<nsPIDOMWindowOuter> win = mContainer->GetWindow()) {
 | 
						|
          win->Close();
 | 
						|
        }
 | 
						|
      }
 | 
						|
    } else if (mClosingWhilePrinting) {
 | 
						|
      if (mDocument) {
 | 
						|
        mDocument->Destroy();
 | 
						|
        mDocument = nullptr;
 | 
						|
      }
 | 
						|
      mClosingWhilePrinting = false;
 | 
						|
    }
 | 
						|
  }
 | 
						|
#endif  // NS_PRINTING && NS_PRINT_PREVIEW
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP nsDocumentViewer::SetPrintSettingsForSubdocument(
 | 
						|
    nsIPrintSettings* aPrintSettings, RemotePrintJobChild* aRemotePrintJob) {
 | 
						|
#ifdef NS_PRINTING
 | 
						|
  {
 | 
						|
    nsAutoScriptBlocker scriptBlocker;
 | 
						|
 | 
						|
    if (mPresShell) {
 | 
						|
      DestroyPresShell();
 | 
						|
    }
 | 
						|
 | 
						|
    if (mPresContext) {
 | 
						|
      DestroyPresContext();
 | 
						|
    }
 | 
						|
 | 
						|
    MOZ_ASSERT(!mPresContext);
 | 
						|
    MOZ_ASSERT(!mPresShell);
 | 
						|
 | 
						|
    if (MOZ_UNLIKELY(!mDocument)) {
 | 
						|
      return NS_ERROR_NOT_AVAILABLE;
 | 
						|
    }
 | 
						|
 | 
						|
    RefPtr<nsDeviceContextSpecProxy> devspec =
 | 
						|
        new nsDeviceContextSpecProxy(aRemotePrintJob);
 | 
						|
    nsresult rv = devspec->Init(aPrintSettings, /* aIsPrintPreview = */ true);
 | 
						|
    NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
    mDeviceContext = new nsDeviceContext();
 | 
						|
    rv = mDeviceContext->InitForPrinting(devspec);
 | 
						|
 | 
						|
    NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
    mPresContext = CreatePresContext(
 | 
						|
        mDocument, nsPresContext::eContext_PrintPreview, FindContainerView());
 | 
						|
    mPresContext->SetPrintSettings(aPrintSettings);
 | 
						|
    rv = mPresContext->Init(mDeviceContext);
 | 
						|
    NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
    rv = MakeWindow(nsSize(mPresContext->DevPixelsToAppUnits(mBounds.width),
 | 
						|
                           mPresContext->DevPixelsToAppUnits(mBounds.height)),
 | 
						|
                    FindContainerView());
 | 
						|
    NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
    MOZ_TRY(InitPresentationStuff(true));
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<PresShell> shell = mPresShell;
 | 
						|
  shell->FlushPendingNotifications(FlushType::Layout);
 | 
						|
#endif
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP nsDocumentViewer::SetPageModeForTesting(
 | 
						|
    bool aPageMode, nsIPrintSettings* aPrintSettings) {
 | 
						|
  // XXX Page mode is only partially working; it's currently used for
 | 
						|
  // reftests that require a paginated context
 | 
						|
  mIsPageMode = aPageMode;
 | 
						|
 | 
						|
  // The DestroyPresShell call requires a script blocker, since the
 | 
						|
  // PresShell::Destroy call it does can cause scripts to run, which could
 | 
						|
  // re-entrantly call methods on the nsDocumentViewer.
 | 
						|
  nsAutoScriptBlocker scriptBlocker;
 | 
						|
 | 
						|
  if (mPresShell) {
 | 
						|
    DestroyPresShell();
 | 
						|
  }
 | 
						|
 | 
						|
  if (mPresContext) {
 | 
						|
    DestroyPresContext();
 | 
						|
  }
 | 
						|
 | 
						|
  mViewManager = nullptr;
 | 
						|
  mWindow = nullptr;
 | 
						|
 | 
						|
  NS_ENSURE_STATE(mDocument);
 | 
						|
  if (aPageMode) {
 | 
						|
    mPresContext = CreatePresContext(
 | 
						|
        mDocument, nsPresContext::eContext_PageLayout, FindContainerView());
 | 
						|
    NS_ENSURE_TRUE(mPresContext, NS_ERROR_OUT_OF_MEMORY);
 | 
						|
    mPresContext->SetPaginatedScrolling(true);
 | 
						|
    mPresContext->SetPrintSettings(aPrintSettings);
 | 
						|
    nsresult rv = mPresContext->Init(mDeviceContext);
 | 
						|
    NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
  }
 | 
						|
  NS_ENSURE_SUCCESS(InitInternal(mParentWidget, nullptr, nullptr, mBounds, true,
 | 
						|
                                 false, false),
 | 
						|
                    NS_ERROR_FAILURE);
 | 
						|
 | 
						|
  Show();
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::GetHistoryEntry(nsISHEntry** aHistoryEntry) {
 | 
						|
  NS_IF_ADDREF(*aHistoryEntry = mSHEntry);
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::GetIsTabModalPromptAllowed(bool* aAllowed) {
 | 
						|
  *aAllowed = !mHidden;
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::GetIsHidden(bool* aHidden) {
 | 
						|
  *aHidden = mHidden;
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentViewer::SetIsHidden(bool aHidden) {
 | 
						|
  mHidden = aHidden;
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
void nsDocumentViewer::DestroyPresShell() {
 | 
						|
  // We assert this because destroying the pres shell could otherwise cause
 | 
						|
  // re-entrancy into nsDocumentViewer methods, and all callers of
 | 
						|
  // DestroyPresShell need to do other cleanup work afterwards before it
 | 
						|
  // is safe for those re-entrant method calls to be made.
 | 
						|
  MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(),
 | 
						|
             "DestroyPresShell must only be called when scripts are blocked");
 | 
						|
 | 
						|
  // Break circular reference (or something)
 | 
						|
  mPresShell->EndObservingDocument();
 | 
						|
 | 
						|
  RefPtr<mozilla::dom::Selection> selection = GetDocumentSelection();
 | 
						|
  if (selection && mSelectionListener)
 | 
						|
    selection->RemoveSelectionListener(mSelectionListener);
 | 
						|
 | 
						|
  mPresShell->Destroy();
 | 
						|
  mPresShell = nullptr;
 | 
						|
}
 | 
						|
 | 
						|
void nsDocumentViewer::InvalidatePotentialSubDocDisplayItem() {
 | 
						|
  if (mViewManager) {
 | 
						|
    if (nsView* rootView = mViewManager->GetRootView()) {
 | 
						|
      if (nsView* rootViewParent = rootView->GetParent()) {
 | 
						|
        if (nsView* subdocview = rootViewParent->GetParent()) {
 | 
						|
          if (nsIFrame* f = subdocview->GetFrame()) {
 | 
						|
            if (nsSubDocumentFrame* s = do_QueryFrame(f)) {
 | 
						|
              s->MarkNeedsDisplayItemRebuild();
 | 
						|
            }
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void nsDocumentViewer::DestroyPresContext() {
 | 
						|
  InvalidatePotentialSubDocDisplayItem();
 | 
						|
  mPresContext = nullptr;
 | 
						|
}
 | 
						|
 | 
						|
void nsDocumentViewer::SetPrintPreviewPresentation(nsViewManager* aViewManager,
 | 
						|
                                                   nsPresContext* aPresContext,
 | 
						|
                                                   PresShell* aPresShell) {
 | 
						|
  // Protect against pres shell destruction running scripts and re-entrantly
 | 
						|
  // creating a new presentation.
 | 
						|
  nsAutoScriptBlocker scriptBlocker;
 | 
						|
 | 
						|
  if (mPresShell) {
 | 
						|
    DestroyPresShell();
 | 
						|
  }
 | 
						|
 | 
						|
  mWindow = nullptr;
 | 
						|
  mViewManager = aViewManager;
 | 
						|
  mPresContext = aPresContext;
 | 
						|
  mPresShell = aPresShell;
 | 
						|
 | 
						|
  if (ShouldAttachToTopLevel()) {
 | 
						|
    DetachFromTopLevelWidget();
 | 
						|
    nsView* rootView = mViewManager->GetRootView();
 | 
						|
    rootView->AttachToTopLevelWidget(mParentWidget);
 | 
						|
    mAttachedToParent = true;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
// Fires the "document-shown" event so that interested parties are aware of it.
 | 
						|
NS_IMETHODIMP
 | 
						|
nsDocumentShownDispatcher::Run() {
 | 
						|
  nsCOMPtr<nsIObserverService> observerService =
 | 
						|
      mozilla::services::GetObserverService();
 | 
						|
  if (observerService) {
 | 
						|
    observerService->NotifyObservers(ToSupports(mDocument), "document-shown",
 | 
						|
                                     nullptr);
 | 
						|
  }
 | 
						|
  return NS_OK;
 | 
						|
}
 |