summaryrefslogtreecommitdiff
path: root/layout/base/nsDocumentViewer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/base/nsDocumentViewer.cpp')
-rw-r--r--layout/base/nsDocumentViewer.cpp4668
1 files changed, 4668 insertions, 0 deletions
diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp
new file mode 100644
index 0000000000..a1105ae524
--- /dev/null
+++ b/layout/base/nsDocumentViewer.cpp
@@ -0,0 +1,4668 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et 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 "mozilla/ServoStyleSet.h"
+#include "nsAutoPtr.h"
+#include "nscore.h"
+#include "nsCOMPtr.h"
+#include "nsCRT.h"
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsIContent.h"
+#include "nsIContentViewerContainer.h"
+#include "nsIContentViewer.h"
+#include "nsIDocumentViewerPrint.h"
+#include "nsIDOMBeforeUnloadEvent.h"
+#include "nsIDocument.h"
+#include "nsPresContext.h"
+#include "nsIPresShell.h"
+#include "mozilla/StyleSetHandle.h"
+#include "mozilla/StyleSetHandleInlines.h"
+#include "nsIFrame.h"
+#include "nsIWritablePropertyBag2.h"
+#include "nsSubDocumentFrame.h"
+
+#include "nsILinkHandler.h"
+#include "nsIDOMDocument.h"
+#include "nsISelectionListener.h"
+#include "mozilla/dom/Selection.h"
+#include "nsIDOMHTMLDocument.h"
+#include "nsIDOMHTMLElement.h"
+#include "nsContentUtils.h"
+#include "nsLayoutStylesheetCache.h"
+#ifdef ACCESSIBILITY
+#include "mozilla/a11y/DocAccessible.h"
+#endif
+#include "mozilla/BasicEvents.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/dom/EncodingUtils.h"
+#include "mozilla/WeakPtr.h"
+#include "mozilla/StyleSheet.h"
+#include "mozilla/StyleSheetInlines.h"
+
+#include "nsViewManager.h"
+#include "nsView.h"
+
+#include "nsIPageSequenceFrame.h"
+#include "nsNetUtil.h"
+#include "nsIContentViewerEdit.h"
+#include "nsIContentViewerFile.h"
+#include "mozilla/StyleSheetInlines.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 "nsIDOMHTMLFrameSetElement.h"
+#include "nsIDOMHTMLImageElement.h"
+#ifdef MOZ_XUL
+#include "nsIXULDocument.h"
+#include "nsXULPopupManager.h"
+#endif
+
+#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 "nsRenderingContext.h"
+#include "nsILoadContext.h"
+
+#include "nsIPrompt.h"
+#include "imgIContainer.h" // image animation mode constants
+
+#include "nsSandboxFlags.h"
+
+#include "mozilla/DocLoadingTimelineMarker.h"
+
+//--------------------------
+// Printing Include
+//---------------------------
+#ifdef NS_PRINTING
+
+#include "nsIWebBrowserPrint.h"
+
+#include "nsPrintEngine.h"
+
+// Print Options
+#include "nsIPrintSettings.h"
+#include "nsIPrintSettingsService.h"
+#include "nsISimpleEnumerator.h"
+
+#include "nsIPluginDocument.h"
+
+#endif // NS_PRINTING
+
+//focus
+#include "nsIDOMEventTarget.h"
+#include "nsIDOMEventListener.h"
+#include "nsISelectionController.h"
+
+#include "mozilla/EventDispatcher.h"
+#include "nsISHEntry.h"
+#include "nsISHistory.h"
+#include "nsISHistoryInternal.h"
+#include "nsIWebNavigation.h"
+#include "mozilla/dom/XMLHttpRequestMainThread.h"
+
+//paint forcing
+#include <stdio.h>
+
+#include "mozilla/dom/Element.h"
+#include "mozilla/Telemetry.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+#define BEFOREUNLOAD_DISABLED_PREFNAME "dom.disable_beforeunload"
+#define BEFOREUNLOAD_REQUIRES_INTERACTION_PREFNAME "dom.require_user_interaction_for_beforeunload"
+
+//-----------------------------------------------------
+// LOGGING
+#include "LayoutLogging.h"
+#include "mozilla/Logging.h"
+
+#ifdef NS_PRINTING
+static 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;
+namespace mozilla {
+class AutoPrintEventDispatcher;
+}
+
+// a small delegate class used to avoid circular references
+
+class nsDocViewerSelectionListener : public nsISelectionListener
+{
+public:
+
+ // nsISupports interface...
+ NS_DECL_ISUPPORTS
+
+ // nsISelectionListerner interface
+ NS_DECL_NSISELECTIONLISTENER
+
+ nsDocViewerSelectionListener()
+ : mDocViewer(nullptr)
+ , mSelectionWasCollapsed(true)
+ {
+ }
+
+ nsresult Init(nsDocumentViewer *aDocViewer);
+
+ void Disconnect() { mDocViewer = nullptr; }
+
+protected:
+
+ virtual ~nsDocViewerSelectionListener() {}
+
+ nsDocumentViewer* mDocViewer;
+ bool mSelectionWasCollapsed;
+
+};
+
+
+/** editor Implementation of the FocusListener interface
+ */
+class nsDocViewerFocusListener : public nsIDOMEventListener
+{
+public:
+ /** default constructor
+ */
+ nsDocViewerFocusListener();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDOMEVENTLISTENER
+
+ nsresult Init(nsDocumentViewer *aDocViewer);
+
+ void Disconnect() { mDocViewer = nullptr; }
+
+protected:
+ /** default destructor
+ */
+ virtual ~nsDocViewerFocusListener();
+
+private:
+ nsDocumentViewer* mDocViewer;
+};
+
+
+//-------------------------------------------------------------
+class nsDocumentViewer final : public nsIContentViewer,
+ public nsIContentViewerEdit,
+ public nsIContentViewerFile,
+ public nsIDocumentViewerPrint
+
+#ifdef NS_PRINTING
+ , public nsIWebBrowserPrint
+#endif
+
+{
+ friend class nsDocViewerSelectionListener;
+ friend class nsPagePrintTimer;
+ friend class nsPrintEngine;
+
+public:
+ nsDocumentViewer();
+
+ NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
+
+ // nsISupports interface...
+ NS_DECL_ISUPPORTS
+
+ // nsIContentViewer interface...
+ NS_DECL_NSICONTENTVIEWER
+
+ // nsIContentViewerEdit
+ NS_DECL_NSICONTENTVIEWEREDIT
+
+ // nsIContentViewerFile
+ NS_DECL_NSICONTENTVIEWERFILE
+
+#ifdef NS_PRINTING
+ // nsIWebBrowserPrint
+ NS_DECL_NSIWEBBROWSERPRINT
+#endif
+
+ typedef void (*CallChildFunc)(nsIContentViewer* aViewer, void* aClosure);
+ void CallChildren(CallChildFunc aFunc, void* aClosure);
+
+ // 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,
+ 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
+ */
+ nsresult InitPresentationStuff(bool aDoInitialReflow);
+
+ nsresult GetPopupNode(nsIDOMNode** aNode);
+ nsresult GetPopupLinkNode(nsIDOMNode** aNode);
+ nsresult GetPopupImageNode(nsIImageLoadingContent** aNode);
+
+ nsresult GetContentSizeInternal(int32_t* aWidth, int32_t* aHeight,
+ nscoord aMaxWidth, nscoord aMaxHeight);
+
+ void PrepareToStartLoad(void);
+
+ nsresult SyncParentSubDocMap();
+
+ mozilla::dom::Selection* GetDocumentSelection();
+
+ void DestroyPresShell();
+ void DestroyPresContext();
+
+#ifdef NS_PRINTING
+ // Called when the DocViewer is notified that the state
+ // of Printing or PP has changed
+ void SetIsPrintingInDocShellTree(nsIDocShellTreeItem* aParentNode,
+ bool aIsPrintingOrPP,
+ bool aStartAtTop);
+#endif // NS_PRINTING
+
+ // 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();
+
+protected:
+ // These return the current shell/prescontext etc.
+ nsIPresShell* GetPresShell();
+ nsPresContext* GetPresContext();
+ 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!
+ nsWeakPtr mTopContainerWhilePrinting;
+ 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<nsIDocument> mDocument;
+ nsCOMPtr<nsIWidget> mWindow; // may be null
+ RefPtr<nsViewManager> mViewManager;
+ RefPtr<nsPresContext> mPresContext;
+ nsCOMPtr<nsIPresShell> mPresShell;
+
+ RefPtr<nsDocViewerSelectionListener> mSelectionListener;
+ RefPtr<nsDocViewerFocusListener> mFocusListener;
+
+ nsCOMPtr<nsIContentViewer> mPreviousViewer;
+ nsCOMPtr<nsISHEntry> mSHEntry;
+
+ nsIWidget* mParentWidget; // purposely won't be ref counted. May be null
+ bool mAttachedToParent; // view is attached to the parent widget
+
+ nsIntRect mBounds;
+
+ // mTextZoom/mPageZoom record the textzoom/pagezoom of the first (galley)
+ // presshell only.
+ float mTextZoom; // Text zoom, defaults to 1.0
+ float mPageZoom;
+ float mOverrideDPPX; // DPPX overrided, defaults to 0.0
+ int mMinFontSize;
+
+ int16_t mNumURLStarts;
+ int16_t mDestroyRefCount; // a second "refcount" for the document viewer's "destroy"
+
+ 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
+ unsigned mPrintPreviewZoomed : 1;
+
+ // These data members support delayed printing when the document is loading
+ unsigned mPrintIsPending : 1;
+ unsigned mPrintDocIsFullyLoaded : 1;
+ nsCOMPtr<nsIPrintSettings> mCachedPrintSettings;
+ nsCOMPtr<nsIWebProgressListener> mCachedPrintWebProgressListner;
+
+ RefPtr<nsPrintEngine> mPrintEngine;
+ float mOriginalPrintPreviewScale;
+ float mPrintPreviewZoom;
+ nsAutoPtr<AutoPrintEventDispatcher> mAutoBeforeAndAfterPrint;
+#endif // NS_PRINT_PREVIEW
+
+#ifdef DEBUG
+ FILE* mDebugFile;
+#endif // DEBUG
+#endif // NS_PRINTING
+
+ /* character set member data */
+ int32_t mHintCharsetSource;
+ nsCString mHintCharset;
+ nsCString mForceCharacterSet;
+
+ bool mIsPageMode;
+ bool mInitializedForPrintPreview;
+ bool mHidden;
+};
+
+namespace mozilla {
+
+/**
+ * A RAII class for automatic dispatch of the 'beforeprint' and 'afterprint'
+ * events ('beforeprint' on construction, 'afterprint' on destruction).
+ *
+ * https://developer.mozilla.org/en-US/docs/Web/Events/beforeprint
+ * https://developer.mozilla.org/en-US/docs/Web/Events/afterprint
+ */
+class AutoPrintEventDispatcher
+{
+public:
+ explicit AutoPrintEventDispatcher(nsIDocument* aTop) : mTop(aTop)
+ {
+ DispatchEventToWindowTree(NS_LITERAL_STRING("beforeprint"));
+ }
+ ~AutoPrintEventDispatcher()
+ {
+ DispatchEventToWindowTree(NS_LITERAL_STRING("afterprint"));
+ }
+
+private:
+ void DispatchEventToWindowTree(const nsAString& aEvent)
+ {
+ nsCOMArray<nsIDocument> targets;
+ CollectDocuments(mTop, &targets);
+ for (int32_t i = 0; i < targets.Count(); ++i) {
+ nsIDocument* d = targets[i];
+ nsContentUtils::DispatchTrustedEvent(d, d->GetWindow(),
+ aEvent, false, false, nullptr);
+ }
+ }
+
+ static bool CollectDocuments(nsIDocument* aDocument, void* aData)
+ {
+ if (aDocument) {
+ static_cast<nsCOMArray<nsIDocument>*>(aData)->AppendObject(aDocument);
+ aDocument->EnumerateSubDocuments(CollectDocuments, aData);
+ }
+ return true;
+ }
+
+ nsCOMPtr<nsIDocument> mTop;
+};
+
+}
+
+class nsDocumentShownDispatcher : public Runnable
+{
+public:
+ explicit nsDocumentShownDispatcher(nsCOMPtr<nsIDocument> aDocument)
+ : mDocument(aDocument) {}
+
+ NS_IMETHOD Run() override;
+
+private:
+ nsCOMPtr<nsIDocument> mDocument;
+};
+
+
+//------------------------------------------------------------------
+// nsDocumentViewer
+//------------------------------------------------------------------
+
+//------------------------------------------------------------------
+already_AddRefed<nsIContentViewer>
+NS_NewContentViewer()
+{
+ RefPtr<nsDocumentViewer> viewer = new nsDocumentViewer();
+ return viewer.forget();
+}
+
+void nsDocumentViewer::PrepareToStartLoad()
+{
+ mStopped = false;
+ mLoaded = false;
+ mAttachedToParent = false;
+ mDeferredWindowClose = false;
+
+#ifdef NS_PRINTING
+ mPrintIsPending = false;
+ mPrintDocIsFullyLoaded = false;
+ mClosingWhilePrinting = false;
+
+ // Make sure we have destroyed it and cleared the data member
+ if (mPrintEngine) {
+ mPrintEngine->Destroy();
+ mPrintEngine = nullptr;
+#ifdef NS_PRINT_PREVIEW
+ SetIsPrintPreview(false);
+#endif
+ }
+
+#ifdef DEBUG
+ mDebugFile = nullptr;
+#endif
+
+#endif // NS_PRINTING
+}
+
+// Note: operator new zeros our memory, so no need to init things to null.
+nsDocumentViewer::nsDocumentViewer()
+ : mTextZoom(1.0), mPageZoom(1.0), mOverrideDPPX(0.0), mMinFontSize(0),
+ mIsSticky(true),
+#ifdef NS_PRINT_PREVIEW
+ mPrintPreviewZoom(1.0),
+#endif
+ mHintCharsetSource(kCharsetUninitialized),
+ 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(nsIContentViewerFile)
+ 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();
+ }
+
+ 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();
+ }
+
+ if (mFocusListener) {
+ mFocusListener->Disconnect();
+ }
+
+ // 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(nsIDocument* aDocument)
+{
+ MOZ_ASSERT(aDocument);
+
+ if (!mDocument) {
+ mDocument = aDocument;
+ }
+}
+
+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->GetParent(getter_AddRefs(parent));
+
+ nsCOMPtr<nsPIDOMWindowOuter> parent_win = parent ? parent->GetWindow() : nullptr;
+ if (!parent_win) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIDocument> parent_doc = parent_win->GetDoc();
+ if (!parent_doc) {
+ return NS_OK;
+ }
+
+ if (mDocument &&
+ parent_doc->GetSubDocumentFor(element) != mDocument &&
+ parent_doc->EventHandlingSuppressed()) {
+ mDocument->SuppressEventHandling(nsIDocument::eEvents,
+ parent_doc->EventHandlingSuppressed());
+ }
+ return parent_doc->SetSubDocumentFor(element, mDocument);
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::SetContainer(nsIDocShell* aContainer)
+{
+ mContainer = static_cast<nsDocShell*>(aContainer);
+ if (mPresContext) {
+ mPresContext->SetContainer(mContainer);
+ }
+
+ // 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)
+{
+ return InitInternal(aParentWidget, nullptr, 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");
+
+ if (GetIsPrintPreview())
+ return NS_OK;
+
+ NS_ASSERTION(!mPresShell,
+ "Someone should have destroyed the presshell!");
+
+ // Create the style set...
+ StyleSetHandle styleSet = CreateStyleSet(mDocument);
+
+ // Now make the shell for the document
+ mPresShell = mDocument->CreateShell(mPresContext, mViewManager, styleSet);
+ if (!mPresShell) {
+ styleSet->Delete();
+ return NS_ERROR_FAILURE;
+ }
+
+ // We're done creating the style set
+ styleSet->EndUpdate();
+
+ 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(Flush_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->SetTextZoom(mTextZoom);
+ mPresContext->SetFullZoom(mPageZoom);
+ mPresContext->SetOverrideDPPX(mOverrideDPPX);
+ mPresContext->SetBaseMinFontSize(mMinFontSize);
+
+ p2a = mPresContext->AppUnitsPerDevPixel(); // zoom may have changed it
+ width = p2a * mBounds.width;
+ height = p2a * mBounds.height;
+ if (aDoInitialReflow) {
+ nsCOMPtr<nsIPresShell> shell = mPresShell;
+ // Initial reflow
+ shell->Initialize(width, height);
+ } else {
+ // Store the visible area so it's available for other callers of
+ // Initialize, like nsContentSink::StartLayout.
+ mPresContext->SetVisibleArea(nsRect(0, 0, width, height));
+ }
+
+ // now register ourselves as a selection listener, so that we get
+ // called when the selection changes in the window
+ if (!mSelectionListener) {
+ nsDocViewerSelectionListener *selectionListener =
+ new nsDocViewerSelectionListener();
+
+ selectionListener->Init(this);
+
+ // mSelectionListener is a owning reference
+ mSelectionListener = selectionListener;
+ }
+
+ RefPtr<mozilla::dom::Selection> selection = GetDocumentSelection();
+ if (!selection) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv = selection->AddSelectionListener(mSelectionListener);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Save old listener so we can unregister it
+ RefPtr<nsDocViewerFocusListener> oldFocusListener = mFocusListener;
+ if (oldFocusListener) {
+ oldFocusListener->Disconnect();
+ }
+
+ // focus listener
+ //
+ // now register ourselves as a focus listener, so that we get called
+ // when the focus changes in the window
+ nsDocViewerFocusListener *focusListener = new nsDocViewerFocusListener();
+
+ focusListener->Init(this);
+
+ // mFocusListener is a strong reference
+ mFocusListener = focusListener;
+
+ if (mDocument) {
+ mDocument->AddEventListener(NS_LITERAL_STRING("focus"),
+ mFocusListener,
+ false, false);
+ mDocument->AddEventListener(NS_LITERAL_STRING("blur"),
+ mFocusListener,
+ false, false);
+
+ if (oldFocusListener) {
+ mDocument->RemoveEventListener(NS_LITERAL_STRING("focus"),
+ oldFocusListener, false);
+ mDocument->RemoveEventListener(NS_LITERAL_STRING("blur"),
+ oldFocusListener, false);
+ }
+ }
+
+ if (aDoInitialReflow && mDocument) {
+ mDocument->ScrollToRef();
+ }
+
+ return NS_OK;
+}
+
+static nsPresContext*
+CreatePresContext(nsIDocument* 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,
+ const nsIntRect& aBounds,
+ bool aDoCreation,
+ bool aNeedMakeCX /*= true*/,
+ bool aForceSetNewDocument /* = true*/)
+{
+ if (mIsPageMode) {
+ // XXXbz should the InitInternal in SetPageMode just pass false
+ // here itself?
+ aForceSetNewDocument = false;
+ }
+
+ // 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()->GetShell()))) {
+ // Create presentation context
+ if (mIsPageMode) {
+ //Presentation context already created in SetPageMode 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) {
+ if (mPresContext) {
+ nsCOMPtr<nsILinkHandler> linkHandler;
+ requestor->GetInterface(NS_GET_IID(nsILinkHandler),
+ getter_AddRefs(linkHandler));
+
+ mPresContext->SetContainer(mContainer);
+ mPresContext->SetLinkHandler(linkHandler);
+ }
+
+ // Set script-context-owner in the document
+
+ nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(requestor);
+
+ if (window) {
+ nsCOMPtr<nsIDocument> curDoc = window->GetExtantDoc();
+ if (aForceSetNewDocument || curDoc != mDocument) {
+ rv = window->SetNewDocument(mDocument, aState, false);
+ if (NS_FAILED(rv)) {
+ Destroy();
+ return rv;
+ }
+ nsJSContext::LoadStart();
+ }
+ }
+ }
+
+ 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
+ nsCOMPtr<nsIPresShell> shell = mPresShell;
+ shell->FlushPendingNotifications(Flush_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)) {
+ 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.
+
+ nsIDocShell *docShell = window->GetDocShell();
+ NS_ENSURE_TRUE(docShell, NS_ERROR_UNEXPECTED);
+
+ docShell->GetRestoringDocument(&restoring);
+ if (!restoring) {
+ NS_ASSERTION(mDocument->IsXULDocument() || // readyState for XUL is bogus
+ mDocument->GetReadyStateEnum() ==
+ nsIDocument::READYSTATE_INTERACTIVE ||
+ // test_stricttransportsecurity.html has old-style
+ // docshell-generated about:blank docs reach this code!
+ (mDocument->GetReadyStateEnum() ==
+ nsIDocument::READYSTATE_UNINITIALIZED &&
+ NS_IsAboutBlank(mDocument->GetDocumentURI())),
+ "Bad readystate");
+ nsCOMPtr<nsIDocument> d = mDocument;
+ mDocument->SetReadyStateInternal(nsIDocument::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(d,
+ nsContentUtils::IsSystemPrincipal(principal) ?
+ "chrome-document-loaded" :
+ "content-document-loaded",
+ nullptr);
+ }
+
+ // Notify any devtools about the load.
+ RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
+
+ if (timelines && timelines->HasConsumer(docShell)) {
+ timelines->AddMarkerForDocShell(docShell,
+ MakeUnique<DocLoadingTimelineMarker>("document::Load"));
+ }
+
+ EventDispatcher::Dispatch(window, mPresContext, &event, nullptr, &status);
+ if (timing) {
+ timing->NotifyLoadEventEnd();
+ }
+ }
+ } else {
+ // XXX: Should fire error event to the document...
+ }
+
+ // 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.
+ if (mDocument) {
+ // 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) {
+ mDocument->ScrollToRef();
+ }
+
+ // Now that the document has loaded, we can tell the presshell
+ // to unsuppress painting.
+ if (mPresShell) {
+ nsCOMPtr<nsIPresShell> shell(mPresShell);
+ shell->UnsuppressPainting();
+ // mPresShell could have been removed now, see bug 378682/421432
+ if (mPresShell) {
+ mPresShell->LoadComplete();
+ }
+ }
+ }
+
+ nsJSContext::LoadEnd();
+
+#ifdef NS_PRINTING
+ // Check to see if someone tried to print during the load
+ if (mPrintIsPending) {
+ mPrintIsPending = false;
+ mPrintDocIsFullyLoaded = true;
+ Print(mCachedPrintSettings, mCachedPrintWebProgressListner);
+ mCachedPrintSettings = nullptr;
+ mCachedPrintWebProgressListner = nullptr;
+ }
+#endif
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::GetLoadCompleted(bool *aOutLoadCompleted)
+{
+ *aOutLoadCompleted = mLoaded;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::PermitUnload(bool *aPermitUnload)
+{
+ bool shouldPrompt = true;
+ return PermitUnloadInternal(&shouldPrompt, aPermitUnload);
+}
+
+
+nsresult
+nsDocumentViewer::PermitUnloadInternal(bool *aShouldPrompt,
+ bool *aPermitUnload)
+{
+ AutoDontWarnAboutSyncXHR disableSyncXHRWarning;
+
+ nsresult rv = NS_OK;
+ *aPermitUnload = true;
+
+ if (!mDocument
+ || mInPermitUnload
+ || mInPermitUnloadPrompt) {
+ return NS_OK;
+ }
+
+ static bool sIsBeforeUnloadDisabled;
+ static bool sBeforeUnloadRequiresInteraction;
+ static bool sBeforeUnloadPrefsCached = false;
+
+ if (!sBeforeUnloadPrefsCached) {
+ sBeforeUnloadPrefsCached = true;
+ Preferences::AddBoolVarCache(&sIsBeforeUnloadDisabled,
+ BEFOREUNLOAD_DISABLED_PREFNAME);
+ Preferences::AddBoolVarCache(&sBeforeUnloadRequiresInteraction,
+ BEFOREUNLOAD_REQUIRES_INTERACTION_PREFNAME);
+ }
+
+ // First, get the script global object from the document...
+ nsPIDOMWindowOuter* window = mDocument->GetWindow();
+
+ if (!window) {
+ // This is odd, but not fatal
+ NS_WARNING("window not set for document!");
+ return NS_OK;
+ }
+
+ NS_ASSERTION(nsContentUtils::IsSafeToRunScript(), "This is unsafe");
+
+ // Now, fire an BeforeUnload event to the document and see if it's ok
+ // to unload...
+ nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(mDocument);
+ nsCOMPtr<nsIDOMEvent> event;
+ domDoc->CreateEvent(NS_LITERAL_STRING("beforeunloadevent"),
+ getter_AddRefs(event));
+ nsCOMPtr<nsIDOMBeforeUnloadEvent> beforeUnload = do_QueryInterface(event);
+ NS_ENSURE_STATE(beforeUnload);
+ event->InitEvent(NS_LITERAL_STRING("beforeunload"), 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);
+
+ bool dialogsAreEnabled = false;
+ {
+ // Never permit popups from the beforeunload handler, no matter
+ // how we get here.
+ nsAutoPopupStatePusher popupStatePusher(openAbused, true);
+
+ // Never permit dialogs from the beforeunload handler
+ nsGlobalWindow* globalWindow = nsGlobalWindow::Cast(window);
+ dialogsAreEnabled = globalWindow->AreDialogsEnabled();
+ nsGlobalWindow::TemporarilyDisableDialogs disableDialogs(globalWindow);
+
+ nsIDocument::PageUnloadingEventTimeStamp timestamp(mDocument);
+
+ mInPermitUnload = true;
+ {
+ Telemetry::AutoTimer<Telemetry::HANDLE_BEFOREUNLOAD_MS> telemetryTimer;
+ EventDispatcher::DispatchDOMEvent(window, nullptr, event, mPresContext,
+ nullptr);
+ }
+ mInPermitUnload = false;
+ }
+
+ nsCOMPtr<nsIDocShell> docShell(mContainer);
+ nsAutoString text;
+ beforeUnload->GetReturnValue(text);
+
+ // NB: we nullcheck mDocument because it might now be dead as a result of
+ // the event being dispatched.
+ if (!sIsBeforeUnloadDisabled && *aShouldPrompt && dialogsAreEnabled &&
+ mDocument && !(mDocument->GetSandboxFlags() & SANDBOXED_MODALS) &&
+ (!sBeforeUnloadRequiresInteraction || mDocument->UserHasInteracted()) &&
+ (event->WidgetEventPtr()->DefaultPrevented() || !text.IsEmpty())) {
+ // Ask the user if it's ok to unload the current page
+
+ nsCOMPtr<nsIPrompt> prompt = do_GetInterface(docShell);
+
+ if (prompt) {
+ nsCOMPtr<nsIWritablePropertyBag2> promptBag = do_QueryInterface(prompt);
+ if (promptBag) {
+ bool isTabModalPromptAllowed;
+ GetIsTabModalPromptAllowed(&isTabModalPromptAllowed);
+ promptBag->SetPropertyAsBool(NS_LITERAL_STRING("allowTabModal"),
+ isTabModalPromptAllowed);
+ }
+
+ nsXPIDLString title, message, stayLabel, leaveLabel;
+ rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
+ "OnBeforeUnloadTitle",
+ title);
+ nsresult tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
+ "OnBeforeUnloadMessage",
+ message);
+ if (NS_FAILED(tmp)) {
+ rv = tmp;
+ }
+ tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
+ "OnBeforeUnloadLeaveButton",
+ leaveLabel);
+ if (NS_FAILED(tmp)) {
+ rv = tmp;
+ }
+ tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
+ "OnBeforeUnloadStayButton",
+ stayLabel);
+ if (NS_FAILED(tmp)) {
+ rv = tmp;
+ }
+
+ if (NS_FAILED(rv) || !title || !message || !stayLabel || !leaveLabel) {
+ NS_ERROR("Failed to get strings from dom.properties!");
+ return NS_OK;
+ }
+
+ // Although the exact value is ignored, we must not pass invalid
+ // bool values through XPConnect.
+ bool dummy = false;
+ int32_t buttonPressed = 0;
+ uint32_t buttonFlags = (nsIPrompt::BUTTON_POS_0_DEFAULT |
+ (nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_0) |
+ (nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_1));
+
+ nsAutoSyncOperation sync(mDocument);
+ mInPermitUnloadPrompt = true;
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::ONBEFOREUNLOAD_PROMPT_COUNT, 1);
+ rv = prompt->ConfirmEx(title, message, buttonFlags,
+ leaveLabel, stayLabel, nullptr, nullptr,
+ &dummy, &buttonPressed);
+ mInPermitUnloadPrompt = false;
+
+ // 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?
+ if (NS_FAILED(rv)) {
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::ONBEFOREUNLOAD_PROMPT_ACTION, 2);
+ *aPermitUnload = false;
+ return NS_OK;
+ }
+
+ // Button 0 == leave, button 1 == stay
+ *aPermitUnload = (buttonPressed == 0);
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::ONBEFOREUNLOAD_PROMPT_ACTION,
+ (*aPermitUnload ? 1 : 0));
+ // If the user decided to go ahead, make sure not to prompt the user again
+ // by toggling the internal prompting bool to false:
+ if (*aPermitUnload) {
+ *aShouldPrompt = false;
+ }
+ }
+ }
+
+ if (docShell) {
+ int32_t childCount;
+ docShell->GetChildCount(&childCount);
+
+ for (int32_t i = 0; i < childCount && *aPermitUnload; ++i) {
+ nsCOMPtr<nsIDocShellTreeItem> item;
+ docShell->GetChildAt(i, getter_AddRefs(item));
+
+ nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(item));
+
+ if (docShell) {
+ nsCOMPtr<nsIContentViewer> cv;
+ docShell->GetContentViewer(getter_AddRefs(cv));
+
+ if (cv) {
+ cv->PermitUnloadInternal(aShouldPrompt, aPermitUnload);
+ }
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+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;
+ }
+
+ 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) {
+ // Poke the GC. The window might be collectable garbage now.
+ nsJSContext::PokeGC(JS::gcreason::PAGE_HIDE, NS_GC_DELAY * 2);
+
+ // if Destroy() was called during OnPageHide(), mDocument is nullptr.
+ NS_ENSURE_STATE(mDocument);
+
+ // First, get the window from the document...
+ nsPIDOMWindowOuter* window = mDocument->GetWindow();
+
+ if (!window) {
+ // Fail if no window is available...
+ NS_WARNING("window not set for document!");
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ // 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.
+ nsAutoPopupStatePusher popupStatePusher(openAbused, true);
+
+ nsIDocument::PageUnloadingEventTimeStamp timestamp(mDocument);
+
+ {
+ Telemetry::AutoTimer<Telemetry::HANDLE_UNLOAD_MS> telemetryTimer;
+ EventDispatcher::Dispatch(window, mPresContext, &event, nullptr, &status);
+ }
+ }
+
+#ifdef MOZ_XUL
+ // look for open menupopups and close them after the unload event, in case
+ // the unload event listeners open any new popups
+ nsContentUtils::HidePopupsInDocument(mDocument);
+#endif
+
+ return NS_OK;
+}
+
+static void
+AttachContainerRecurse(nsIDocShell* aShell)
+{
+ nsCOMPtr<nsIContentViewer> viewer;
+ aShell->GetContentViewer(getter_AddRefs(viewer));
+ if (viewer) {
+ viewer->SetIsHidden(false);
+ nsIDocument* doc = viewer->GetDocument();
+ if (doc) {
+ doc->SetContainer(static_cast<nsDocShell*>(aShell));
+ }
+ RefPtr<nsPresContext> pc;
+ viewer->GetPresContext(getter_AddRefs(pc));
+ if (pc) {
+ pc->SetContainer(static_cast<nsDocShell*>(aShell));
+ nsCOMPtr<nsILinkHandler> handler = do_QueryInterface(aShell);
+ pc->SetLinkHandler(handler);
+ }
+ nsCOMPtr<nsIPresShell> presShell;
+ viewer->GetPresShell(getter_AddRefs(presShell));
+ if (presShell) {
+ presShell->SetForwardingContainer(WeakPtr<nsDocShell>());
+ }
+ }
+
+ // Now recurse through the children
+ int32_t childCount;
+ aShell->GetChildCount(&childCount);
+ for (int32_t i = 0; i < childCount; ++i) {
+ nsCOMPtr<nsIDocShellTreeItem> childItem;
+ aShell->GetChildAt(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, 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();
+
+ if (mFocusListener && mDocument) {
+ mDocument->AddEventListener(NS_LITERAL_STRING("focus"), mFocusListener,
+ false, false);
+ mDocument->AddEventListener(NS_LITERAL_STRING("blur"), mFocusListener,
+ false, false);
+ }
+
+ // 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 defined(NS_PRINTING) && defined(NS_PRINT_PREVIEW)
+ // Turn scripting back on
+ // after PrintPreview had turned it off
+ if (GetIsPrintPreview() && mPrintEngine) {
+ mPrintEngine->TurnScriptingOn(true);
+ }
+#endif
+
+#ifdef NS_PRINTING
+ // A Close was called while we were printing
+ // so don't clear the ScriptGlobalObject
+ // or clear the mDocument below
+ if (mPrintEngine && !mClosingWhilePrinting) {
+ mClosingWhilePrinting = true;
+ } else
+#endif
+ {
+ // out of band cleanup of docshell
+ mDocument->SetScriptGlobalObject(nullptr);
+
+ if (!mSHEntry && mDocument)
+ mDocument->RemovedFromDocShell();
+ }
+
+ if (mFocusListener) {
+ mFocusListener->Disconnect();
+ if (mDocument) {
+ mDocument->RemoveEventListener(NS_LITERAL_STRING("focus"), mFocusListener,
+ false);
+ mDocument->RemoveEventListener(NS_LITERAL_STRING("blur"), mFocusListener,
+ false);
+ }
+ }
+
+ return NS_OK;
+}
+
+static void
+DetachContainerRecurse(nsIDocShell *aShell)
+{
+ // Unhook this docshell's presentation
+ nsCOMPtr<nsIContentViewer> viewer;
+ aShell->GetContentViewer(getter_AddRefs(viewer));
+ if (viewer) {
+ nsIDocument* doc = viewer->GetDocument();
+ if (doc) {
+ doc->SetContainer(nullptr);
+ }
+ RefPtr<nsPresContext> pc;
+ viewer->GetPresContext(getter_AddRefs(pc));
+ if (pc) {
+ pc->Detach();
+ }
+ nsCOMPtr<nsIPresShell> presShell;
+ viewer->GetPresShell(getter_AddRefs(presShell));
+ if (presShell) {
+ auto weakShell = static_cast<nsDocShell*>(aShell);
+ presShell->SetForwardingContainer(weakShell);
+ }
+ }
+
+ // Now recurse through the children
+ int32_t childCount;
+ aShell->GetChildCount(&childCount);
+ for (int32_t i = 0; i < childCount; ++i) {
+ nsCOMPtr<nsIDocShellTreeItem> childItem;
+ aShell->GetChildAt(i, getter_AddRefs(childItem));
+ nsCOMPtr<nsIDocShell> shell = do_QueryInterface(childItem);
+ DetachContainerRecurse(shell);
+ }
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::Destroy()
+{
+ NS_ASSERTION(mDocument, "No document in Destroy()!");
+
+#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 (mPrintEngine) {
+ if (mPrintEngine->CheckBeforeDestroy()) {
+ return NS_OK;
+ }
+ }
+ // Dispatch the 'afterprint' event now, if pending:
+ mAutoBeforeAndAfterPrint = nullptr;
+#endif
+
+ // 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 (mDestroyRefCount != 0) {
+ --mDestroyRefCount;
+ return NS_OK;
+ }
+
+ // 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;
+
+ bool savePresentation = mDocument ? mDocument->IsBFCachingAllowed() : 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) {
+ 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 = mSHEntry; // we'll need this below
+ mSHEntry = nullptr;
+
+ if (savePresentation) {
+ 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();
+
+ // 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 (mPresContext) {
+ mPresContext->Detach();
+ }
+ 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 (mPrintEngine) {
+ RefPtr<nsPrintEngine> printEngine = mozilla::Move(mPrintEngine);
+#ifdef NS_PRINT_PREVIEW
+ bool doingPrintPreview;
+ printEngine->GetDoingPrintPreview(&doingPrintPreview);
+ if (doingPrintPreview) {
+ printEngine->FinishPrintPreview();
+ }
+#endif
+ printEngine->Destroy();
+ MOZ_ASSERT(!mPrintEngine,
+ "mPrintEngine 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();
+ }
+
+ if (!mHidden && (mLoaded || mStopped) && mPresContext && !mSHEntry)
+ mPresContext->SetImageAnimationMode(imgIContainer::kDontAnimMode);
+
+ mStopped = true;
+
+ if (!mLoaded && mPresShell) {
+ // Well, we might as well paint what we have so far.
+ nsCOMPtr<nsIPresShell> shell(mPresShell); // bug 378682
+ shell->UnsuppressPainting();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::GetDOMDocument(nsIDOMDocument **aResult)
+{
+ NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
+ return CallQueryInterface(mDocument, aResult);
+}
+
+NS_IMETHODIMP_(nsIDocument *)
+nsDocumentViewer::GetDocument()
+{
+ return mDocument;
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::SetDOMDocument(nsIDOMDocument *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;
+
+ nsCOMPtr<nsIDocument> newDoc = do_QueryInterface(aDocument);
+ NS_ENSURE_TRUE(newDoc, NS_ERROR_UNEXPECTED);
+
+ return SetDocumentInternal(newDoc, false);
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::SetDocumentInternal(nsIDocument* 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->IsStaticDocument()) {
+ mDocument->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->GetChildCount(&count);
+ for (int32_t i = 0; i < count; ++i) {
+ nsCOMPtr<nsIDocShellTreeItem> child;
+ node->GetChildAt(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, mBounds, true, true, false);
+ }
+
+ return rv;
+}
+
+nsIPresShell*
+nsDocumentViewer::GetPresShell()
+{
+ return mPresShell;
+}
+
+nsPresContext*
+nsDocumentViewer::GetPresContext()
+{
+ return mPresContext;
+}
+
+nsViewManager*
+nsDocumentViewer::GetViewManager()
+{
+ return mViewManager;
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::GetPresShell(nsIPresShell** aResult)
+{
+ nsIPresShell* shell = GetPresShell();
+ NS_IF_ADDREF(*aResult = shell);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::GetPresContext(nsPresContext** aResult)
+{
+ nsPresContext* pc = GetPresContext();
+ NS_IF_ADDREF(*aResult = pc);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::GetBounds(nsIntRect& aResult)
+{
+ NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
+ aResult = mBounds;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::GetPreviousViewer(nsIContentViewer** aViewer)
+{
+ *aViewer = mPreviousViewer;
+ NS_IF_ADDREF(*aViewer);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+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.
+ nsCOMPtr<nsIContentViewer> prevViewer;
+ aViewer->GetPreviousViewer(getter_AddRefs(prevViewer));
+ if (prevViewer) {
+ aViewer->SetPreviousViewer(nullptr);
+ aViewer->Destroy();
+ return SetPreviousViewer(prevViewer);
+ }
+ }
+
+ mPreviousViewer = aViewer;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::SetBoundsWithFlags(const nsIntRect& aBounds, uint32_t aFlags)
+{
+ NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
+
+ 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->UIResolutionChanged();
+ }
+ int32_t p2a = mPresContext->AppUnitsPerDevPixel();
+ mViewManager->SetWindowDimensions(NSIntPixelsToAppUnits(mBounds.width, p2a),
+ NSIntPixelsToAppUnits(mBounds.height, p2a),
+ !!(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(void)
+{
+ 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->GetSameTypeRootTreeItem(getter_AddRefs(root));
+ nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(root);
+ nsCOMPtr<nsISHistory> history;
+ webNav->GetSessionHistory(getter_AddRefs(history));
+ nsCOMPtr<nsISHistoryInternal> historyInt = do_QueryInterface(history);
+ if (historyInt) {
+ int32_t prevIndex,loadedIndex;
+ nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(treeItem);
+ docShell->GetPreviousTransIndex(&prevIndex);
+ docShell->GetLoadedTransIndex(&loadedIndex);
+#ifdef DEBUG_PAGE_CACHE
+ printf("About to evict content viewers: prev=%d, loaded=%d\n",
+ prevIndex, loadedIndex);
+#endif
+ historyInt->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<nsIDocument> 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) {
+ mParentWidget->Release(); // GetParentWidget AddRefs, but mParentWidget is weak
+ }
+ }
+
+ 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 && base_win) {
+ nsCOMPtr<nsILinkHandler> linkHandler(do_GetInterface(base_win));
+
+ if (linkHandler) {
+ mPresContext->SetLinkHandler(linkHandler);
+ }
+
+ mPresContext->SetContainer(mContainer);
+ }
+
+ 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) {
+ nsCOMPtr<nsIPresShell> shell(mPresShell); // bug 378682
+ shell->UnsuppressPainting();
+ }
+ }
+
+ // Notify observers that a new page has been shown. This will get run
+ // from the event loop after we actually draw the page.
+ NS_DispatchToMainThread(new nsDocumentShownDispatcher(document));
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::Hide(void)
+{
+ 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::RequestWindowClose(bool* aCanClose)
+{
+#ifdef NS_PRINTING
+ if (mPrintIsPending || (mPrintEngine && mPrintEngine->GetIsPrinting())) {
+ *aCanClose = false;
+ mDeferredWindowClose = true;
+ } else
+#endif
+ *aCanClose = true;
+
+ return NS_OK;
+}
+
+StyleSetHandle
+nsDocumentViewer::CreateStyleSet(nsIDocument* aDocument)
+{
+ // Make sure this does the same thing as PresShell::AddSheet wrt ordering.
+
+ // this should eventually get expanded to allow for creating
+ // different sets for different media
+
+ StyleBackendType backendType = aDocument->GetStyleBackendType();
+
+ StyleSetHandle styleSet;
+ if (backendType == StyleBackendType::Gecko) {
+ styleSet = new nsStyleSet();
+ } else {
+ styleSet = new ServoStyleSet();
+ }
+
+ styleSet->BeginUpdate();
+
+ // The document will fill in the document sheets when we create the presshell
+
+ if (aDocument->IsBeingUsedAsImage()) {
+ MOZ_ASSERT(aDocument->IsSVGDocument(),
+ "Do we want to skip most sheets for this new image type?");
+
+ // SVG-as-an-image must be kept as light and small as possible. We
+ // deliberately skip loading everything and leave svg.css (and html.css and
+ // xul.css) to be loaded on-demand.
+ // XXXjwatt Nothing else is loaded on-demand, but I don't think that
+ // should matter for SVG-as-an-image. If it does, I want to know why!
+
+ // Caller will handle calling EndUpdate, per contract.
+ return styleSet;
+ }
+
+ auto cache = nsLayoutStylesheetCache::For(backendType);
+
+ // Handle the user sheets.
+ StyleSheet* sheet = nullptr;
+ if (nsContentUtils::IsInChromeDocshell(aDocument)) {
+ sheet = cache->UserChromeSheet();
+ } else {
+ sheet = cache->UserContentSheet();
+ }
+
+ if (sheet)
+ styleSet->AppendStyleSheet(SheetType::User, sheet);
+
+ // Append chrome sheets (scrollbars + forms).
+ bool shouldOverride = false;
+ // We don't want a docshell here for external resource docs, so just
+ // look at mContainer.
+ nsCOMPtr<nsIDocShell> ds(mContainer);
+ nsCOMPtr<nsIDOMEventTarget> chromeHandler;
+ nsCOMPtr<nsIURI> uri;
+ RefPtr<StyleSheet> chromeSheet;
+
+ if (ds) {
+ ds->GetChromeEventHandler(getter_AddRefs(chromeHandler));
+ }
+ if (chromeHandler) {
+ nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(chromeHandler));
+ nsCOMPtr<nsIContent> content(do_QueryInterface(elt));
+ if (elt && content) {
+ nsCOMPtr<nsIURI> baseURI = content->GetBaseURI();
+
+ nsAutoString sheets;
+ elt->GetAttribute(NS_LITERAL_STRING("usechromesheets"), sheets);
+ if (!sheets.IsEmpty() && baseURI) {
+ RefPtr<mozilla::css::Loader> cssLoader =
+ new mozilla::css::Loader(backendType);
+
+ char *str = ToNewCString(sheets);
+ char *newStr = str;
+ char *token;
+ while ( (token = nsCRT::strtok(newStr, ", ", &newStr)) ) {
+ NS_NewURI(getter_AddRefs(uri), nsDependentCString(token), nullptr,
+ baseURI);
+ if (!uri) continue;
+
+ cssLoader->LoadSheetSync(uri, &chromeSheet);
+ if (!chromeSheet) continue;
+
+ styleSet->PrependStyleSheet(SheetType::Agent, chromeSheet);
+ shouldOverride = true;
+ }
+ free(str);
+ }
+ }
+ }
+
+ if (!shouldOverride) {
+ sheet = cache->ScrollbarsSheet();
+ if (sheet) {
+ styleSet->PrependStyleSheet(SheetType::Agent, sheet);
+ }
+ }
+
+ if (!aDocument->IsSVGDocument()) {
+ // !!! IMPORTANT - KEEP THIS BLOCK IN SYNC WITH
+ // !!! SVGDocument::EnsureNonSVGUserAgentStyleSheetsLoaded.
+
+ // SVGForeignObjectElement::BindToTree calls SVGDocument::
+ // EnsureNonSVGUserAgentStyleSheetsLoaded to loads these UA sheet
+ // on-demand. (Excluding the quirks sheet, which should never be loaded for
+ // an SVG document, and excluding xul.css which will be loaded on demand by
+ // nsXULElement::BindToTree.)
+
+ sheet = cache->NumberControlSheet();
+ if (sheet) {
+ styleSet->PrependStyleSheet(SheetType::Agent, sheet);
+ }
+
+ sheet = cache->FormsSheet();
+ if (sheet) {
+ styleSet->PrependStyleSheet(SheetType::Agent, sheet);
+ }
+
+ if (aDocument->LoadsFullXULStyleSheetUpFront()) {
+ // nsXULElement::BindToTree loads xul.css on-demand if we don't load it
+ // up-front here.
+ sheet = cache->XULSheet();
+ if (sheet) {
+ styleSet->PrependStyleSheet(SheetType::Agent, sheet);
+ }
+ }
+
+ sheet = cache->MinimalXULSheet();
+ if (sheet) {
+ // Load the minimal XUL rules for scrollbars and a few other XUL things
+ // that non-XUL (typically HTML) documents commonly use.
+ styleSet->PrependStyleSheet(SheetType::Agent, sheet);
+ }
+
+ sheet = cache->CounterStylesSheet();
+ if (sheet) {
+ styleSet->PrependStyleSheet(SheetType::Agent, sheet);
+ }
+
+ if (nsLayoutUtils::ShouldUseNoScriptSheet(aDocument)) {
+ sheet = cache->NoScriptSheet();
+ if (sheet) {
+ styleSet->PrependStyleSheet(SheetType::Agent, sheet);
+ }
+ }
+
+ if (nsLayoutUtils::ShouldUseNoFramesSheet(aDocument)) {
+ sheet = cache->NoFramesSheet();
+ if (sheet) {
+ styleSet->PrependStyleSheet(SheetType::Agent, sheet);
+ }
+ }
+
+ // We don't add quirk.css here; nsPresContext::CompatibilityModeChanged will
+ // append it if needed.
+
+ sheet = cache->HTMLSheet();
+ if (sheet) {
+ styleSet->PrependStyleSheet(SheetType::Agent, sheet);
+ }
+
+ styleSet->PrependStyleSheet(SheetType::Agent,
+ cache->UASheet());
+ } else {
+ // SVG documents may have scrollbars and need the scrollbar styling.
+ sheet = cache->MinimalXULSheet();
+ if (sheet) {
+ styleSet->PrependStyleSheet(SheetType::Agent, sheet);
+ }
+ }
+
+ if (styleSet->IsGecko()) {
+ nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
+ if (sheetService) {
+ for (StyleSheet* sheet : *sheetService->AgentStyleSheets()) {
+ styleSet->AppendStyleSheet(SheetType::Agent, sheet);
+ }
+ for (StyleSheet* sheet : Reversed(*sheetService->UserStyleSheets())) {
+ styleSet->PrependStyleSheet(SheetType::User, sheet);
+ }
+ }
+ } else {
+ NS_WARNING("stylo: Not yet checking nsStyleSheetService for Servo-backed "
+ "documents. See bug 1290224");
+ }
+
+ // Caller will handle calling EndUpdate, per contract.
+ return styleSet;
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::ClearHistoryEntry()
+{
+ 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
+ nsWidgetInitData initData;
+ nsWidgetInitData* initDataPtr;
+ if (!mParentWidget) {
+ initDataPtr = &initData;
+ initData.mWindowType = eWindowType_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 = nsLayoutUtils::GetRealPrimaryFrameFor(containerElement);
+ if (!subdocFrame) {
+ // XXX Silenced by default in bug 1175289
+ LAYOUT_WARNING("Subdocument container has no frame");
+ return nullptr;
+ }
+
+ // subdocFrame might not be a subdocument frame; the frame
+ // constructor can treat a <frame> as an inline in some XBL
+ // cases. Treat that as display:none, the document is not
+ // displayed.
+ if (subdocFrame->GetType() != nsGkAtoms::subDocumentFrame) {
+ NS_WARNING_ASSERTION(!subdocFrame->GetType(),
+ "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)
+{
+ NS_PRECONDITION(!mPresShell && !mWindow,
+ "This will screw up our existing presentation");
+ NS_PRECONDITION(mDocument, "Gotta have a document here");
+
+ nsIDocument* 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
+ nsIPresShell* shell = doc->GetShell();
+ if (shell) {
+ nsPresContext* ctx = shell->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
+ * ======================================================================================== */
+
+NS_IMETHODIMP nsDocumentViewer::ClearSelection()
+{
+ // use nsCopySupport::GetSelectionForCopy() ?
+ RefPtr<mozilla::dom::Selection> selection = GetDocumentSelection();
+ if (!selection) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return selection->CollapseToStart();
+}
+
+NS_IMETHODIMP nsDocumentViewer::SelectAll()
+{
+ // XXX this is a temporary implementation copied from nsWebShell
+ // for now. I think nsDocument 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;
+ }
+
+ nsCOMPtr<nsIDOMHTMLDocument> htmldoc = do_QueryInterface(mDocument);
+ nsCOMPtr<nsIDOMNode> bodyNode;
+
+ nsresult rv;
+ if (htmldoc)
+ {
+ nsCOMPtr<nsIDOMHTMLElement>bodyElement;
+ rv = htmldoc->GetBody(getter_AddRefs(bodyElement));
+ if (NS_FAILED(rv) || !bodyElement) return rv;
+
+ bodyNode = do_QueryInterface(bodyElement);
+ }
+ else if (mDocument)
+ {
+ bodyNode = do_QueryInterface(mDocument->GetRootElement());
+ }
+ if (!bodyNode) return NS_ERROR_FAILURE;
+
+ rv = selection->RemoveAllRanges();
+ if (NS_FAILED(rv)) return rv;
+
+ mozilla::dom::Selection::AutoUserInitiated userSelection(selection);
+ rv = selection->SelectAllChildren(bodyNode);
+ return rv;
+}
+
+NS_IMETHODIMP nsDocumentViewer::CopySelection()
+{
+ nsCopySupport::FireClipboardEvent(eCopy, nsIClipboard::kGlobalClipboard,
+ mPresShell, nullptr);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsDocumentViewer::CopyLinkLocation()
+{
+ NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_INITIALIZED);
+ nsCOMPtr<nsIDOMNode> node;
+ GetPopupLinkNode(getter_AddRefs(node));
+ // 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(getter_AddRefs(node));
+ // 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:
+ nsCOMPtr<nsISelection> sel;
+ if (selectionOnly) {
+ nsCopySupport::GetSelectionForCopy(mDocument, getter_AddRefs(sel));
+ NS_ENSURE_TRUE(sel, NS_ERROR_FAILURE);
+
+ bool isCollapsed;
+ sel->GetIsCollapsed(&isCollapsed);
+ if (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(nsIDOMNode* aNode)
+{
+ nsIDocument* 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;
+}
+
+/* ========================================================================================
+ * nsIContentViewerFile
+ * ======================================================================================== */
+/** ---------------------------------------------------
+ * See documentation above in the nsIContentViewerfile class definition
+ * @update 01/24/00 dwc
+ */
+NS_IMETHODIMP
+nsDocumentViewer::Print(bool aSilent,
+ FILE * aDebugFile,
+ nsIPrintSettings* aPrintSettings)
+{
+#ifdef NS_PRINTING
+ nsCOMPtr<nsIPrintSettings> printSettings;
+
+#ifdef DEBUG
+ nsresult rv = NS_ERROR_FAILURE;
+
+ mDebugFile = aDebugFile;
+ // if they don't pass in a PrintSettings, then make one
+ // it will have all the default values
+ printSettings = aPrintSettings;
+ nsCOMPtr<nsIPrintSettingsService> printSettingsSvc
+ = do_GetService("@mozilla.org/gfx/printsettings-service;1", &rv);
+ if (NS_SUCCEEDED(rv)) {
+ // if they don't pass in a PrintSettings, then make one
+ if (printSettings == nullptr) {
+ printSettingsSvc->GetNewPrintSettings(getter_AddRefs(printSettings));
+ }
+ NS_ASSERTION(printSettings, "You can't PrintPreview without a PrintSettings!");
+ }
+ if (printSettings) printSettings->SetPrintSilent(aSilent);
+ if (printSettings) printSettings->SetShowPrintProgress(false);
+#endif
+
+
+ return Print(printSettings, nullptr);
+#else
+ return NS_ERROR_FAILURE;
+#endif
+}
+
+// nsIContentViewerFile interface
+NS_IMETHODIMP
+nsDocumentViewer::GetPrintable(bool *aPrintable)
+{
+ NS_ENSURE_ARG_POINTER(aPrintable);
+
+ *aPrintable = !GetIsPrinting();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsDocumentViewer::ScrollToNode(nsIDOMNode* aNode)
+{
+ NS_ENSURE_ARG(aNode);
+ NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
+ nsCOMPtr<nsIPresShell> presShell;
+ NS_ENSURE_SUCCESS(GetPresShell(getter_AddRefs(presShell)), NS_ERROR_FAILURE);
+
+ // Get the nsIContent interface, because that's what we need to
+ // get the primary frame
+
+ nsCOMPtr<nsIContent> content(do_QueryInterface(aNode));
+ NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
+
+ // Tell the PresShell to scroll to the primary frame of the content.
+ NS_ENSURE_SUCCESS(
+ presShell->ScrollContentIntoView(content,
+ nsIPresShell::ScrollAxis(
+ nsIPresShell::SCROLL_TOP,
+ nsIPresShell::SCROLL_ALWAYS),
+ nsIPresShell::ScrollAxis(),
+ nsIPresShell::SCROLL_OVERFLOW_HIDDEN),
+ NS_ERROR_FAILURE);
+ return NS_OK;
+}
+
+void
+nsDocumentViewer::CallChildren(CallChildFunc aFunc, void* aClosure)
+{
+ nsCOMPtr<nsIDocShell> docShell(mContainer);
+ if (docShell)
+ {
+ int32_t i;
+ int32_t n;
+ docShell->GetChildCount(&n);
+ for (i=0; i < n; i++)
+ {
+ nsCOMPtr<nsIDocShellTreeItem> child;
+ docShell->GetChildAt(i, getter_AddRefs(child));
+ nsCOMPtr<nsIDocShell> childAsShell(do_QueryInterface(child));
+ NS_ASSERTION(childAsShell, "null child in docshell");
+ if (childAsShell)
+ {
+ nsCOMPtr<nsIContentViewer> childCV;
+ childAsShell->GetContentViewer(getter_AddRefs(childCV));
+ if (childCV)
+ {
+ (*aFunc)(childCV, aClosure);
+ }
+ }
+ }
+ }
+}
+
+static void
+ChangeChildPaintingEnabled(nsIContentViewer* aChild, void* aClosure)
+{
+ bool* enablePainting = (bool*) aClosure;
+ if (*enablePainting) {
+ aChild->ResumePainting();
+ } else {
+ aChild->PausePainting();
+ }
+}
+
+struct ZoomInfo
+{
+ float mZoom;
+};
+
+static void
+SetChildTextZoom(nsIContentViewer* aChild, void* aClosure)
+{
+ struct ZoomInfo* ZoomInfo = (struct ZoomInfo*) aClosure;
+ aChild->SetTextZoom(ZoomInfo->mZoom);
+}
+
+static void
+SetChildMinFontSize(nsIContentViewer* aChild, void* aClosure)
+{
+ aChild->SetMinFontSize(NS_PTR_TO_INT32(aClosure));
+}
+
+static void
+SetChildFullZoom(nsIContentViewer* aChild, void* aClosure)
+{
+ struct ZoomInfo* ZoomInfo = (struct ZoomInfo*) aClosure;
+ aChild->SetFullZoom(ZoomInfo->mZoom);
+}
+
+static void
+SetChildOverrideDPPX(nsIContentViewer* aChild, void* aClosure)
+{
+ struct ZoomInfo* ZoomInfo = (struct ZoomInfo*) aClosure;
+ aChild->SetOverrideDPPX(ZoomInfo->mZoom);
+}
+
+static bool
+SetExtResourceTextZoom(nsIDocument* aDocument, void* aClosure)
+{
+ // Would it be better to enumerate external resource viewers instead?
+ nsIPresShell* shell = aDocument->GetShell();
+ if (shell) {
+ nsPresContext* ctxt = shell->GetPresContext();
+ if (ctxt) {
+ struct ZoomInfo* ZoomInfo = static_cast<struct ZoomInfo*>(aClosure);
+ ctxt->SetTextZoom(ZoomInfo->mZoom);
+ }
+ }
+
+ return true;
+}
+
+static bool
+SetExtResourceMinFontSize(nsIDocument* aDocument, void* aClosure)
+{
+ nsIPresShell* shell = aDocument->GetShell();
+ if (shell) {
+ nsPresContext* ctxt = shell->GetPresContext();
+ if (ctxt) {
+ ctxt->SetBaseMinFontSize(NS_PTR_TO_INT32(aClosure));
+ }
+ }
+
+ return true;
+}
+
+static bool
+SetExtResourceFullZoom(nsIDocument* aDocument, void* aClosure)
+{
+ // Would it be better to enumerate external resource viewers instead?
+ nsIPresShell* shell = aDocument->GetShell();
+ if (shell) {
+ nsPresContext* ctxt = shell->GetPresContext();
+ if (ctxt) {
+ struct ZoomInfo* ZoomInfo = static_cast<struct ZoomInfo*>(aClosure);
+ ctxt->SetFullZoom(ZoomInfo->mZoom);
+ }
+ }
+
+ return true;
+}
+
+static bool
+SetExtResourceOverrideDPPX(nsIDocument* aDocument, void* aClosure)
+{
+ nsIPresShell* shell = aDocument->GetShell();
+ if (shell) {
+ nsPresContext* ctxt = shell->GetPresContext();
+ if (ctxt) {
+ struct ZoomInfo* ZoomInfo = static_cast<struct ZoomInfo*>(aClosure);
+ ctxt->SetOverrideDPPX(ZoomInfo->mZoom);
+ }
+ }
+
+ return true;
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::SetTextZoom(float aTextZoom)
+{
+ // If we don't have a document, then we need to bail.
+ if (!mDocument) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (GetIsPrintPreview()) {
+ return NS_OK;
+ }
+
+ mTextZoom = aTextZoom;
+
+ // Set the text zoom on all children of mContainer (even if our zoom didn't
+ // change, our children's zoom may be different, though it would be unusual).
+ // Do this first, in case kids are auto-sizing and post reflow commands on
+ // our presshell (which should be subsumed into our own style change reflow).
+ struct ZoomInfo ZoomInfo = { aTextZoom };
+ CallChildren(SetChildTextZoom, &ZoomInfo);
+
+ // Now change our own zoom
+ nsPresContext* pc = GetPresContext();
+ if (pc && aTextZoom != mPresContext->TextZoom()) {
+ pc->SetTextZoom(aTextZoom);
+ }
+
+ // And do the external resources
+ mDocument->EnumerateExternalResources(SetExtResourceTextZoom, &ZoomInfo);
+
+ nsContentUtils::DispatchChromeEvent(mDocument, static_cast<nsIDocument*>(mDocument),
+ NS_LITERAL_STRING("TextZoomChange"),
+ true, true);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::GetTextZoom(float* aTextZoom)
+{
+ NS_ENSURE_ARG_POINTER(aTextZoom);
+ nsPresContext* pc = GetPresContext();
+ *aTextZoom = pc ? pc->TextZoom() : 1.0f;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::SetMinFontSize(int32_t aMinFontSize)
+{
+ // If we don't have a document, then we need to bail.
+ if (!mDocument) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (GetIsPrintPreview()) {
+ return NS_OK;
+ }
+
+ mMinFontSize = aMinFontSize;
+
+ // Set the min font on all children of mContainer (even if our min font didn't
+ // change, our children's min font may be different, though it would be unusual).
+ // Do this first, in case kids are auto-sizing and post reflow commands on
+ // our presshell (which should be subsumed into our own style change reflow).
+ CallChildren(SetChildMinFontSize, NS_INT32_TO_PTR(aMinFontSize));
+
+ // Now change our own min font
+ nsPresContext* pc = GetPresContext();
+ if (pc && aMinFontSize != mPresContext->MinFontSize(nullptr)) {
+ pc->SetBaseMinFontSize(aMinFontSize);
+ }
+
+ // And do the external resources
+ mDocument->EnumerateExternalResources(SetExtResourceMinFontSize,
+ NS_INT32_TO_PTR(aMinFontSize));
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::GetMinFontSize(int32_t* aMinFontSize)
+{
+ NS_ENSURE_ARG_POINTER(aMinFontSize);
+ nsPresContext* pc = GetPresContext();
+ *aMinFontSize = pc ? pc->BaseMinFontSize() : 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::SetFullZoom(float aFullZoom)
+{
+#ifdef NS_PRINT_PREVIEW
+ if (GetIsPrintPreview()) {
+ nsPresContext* pc = GetPresContext();
+ NS_ENSURE_TRUE(pc, NS_OK);
+ nsCOMPtr<nsIPresShell> shell = pc->GetPresShell();
+ NS_ENSURE_TRUE(shell, NS_OK);
+
+ if (!mPrintPreviewZoomed) {
+ mOriginalPrintPreviewScale = pc->GetPrintPreviewScale();
+ mPrintPreviewZoomed = true;
+ }
+
+ mPrintPreviewZoom = aFullZoom;
+ pc->SetPrintPreviewScale(aFullZoom * mOriginalPrintPreviewScale);
+ nsIPageSequenceFrame* pf = shell->GetPageSequenceFrame();
+ if (pf) {
+ nsIFrame* f = do_QueryFrame(pf);
+ shell->FrameNeedsReflow(f, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
+ }
+
+ nsIFrame* rootFrame = shell->GetRootFrame();
+ if (rootFrame) {
+ rootFrame->InvalidateFrame();
+ }
+ return NS_OK;
+ }
+#endif
+
+ // If we don't have a document, then we need to bail.
+ if (!mDocument) {
+ return NS_ERROR_FAILURE;
+ }
+
+ bool fullZoomChange = (mPageZoom != aFullZoom);
+ mPageZoom = aFullZoom;
+
+ struct ZoomInfo ZoomInfo = { aFullZoom };
+ CallChildren(SetChildFullZoom, &ZoomInfo);
+
+ nsPresContext* pc = GetPresContext();
+ if (pc) {
+ pc->SetFullZoom(aFullZoom);
+ }
+
+ // And do the external resources
+ mDocument->EnumerateExternalResources(SetExtResourceFullZoom, &ZoomInfo);
+
+ // Dispatch FullZoomChange event only if fullzoom value really was been changed
+ if (fullZoomChange) {
+ nsContentUtils::DispatchChromeEvent(mDocument, static_cast<nsIDocument*>(mDocument),
+ NS_LITERAL_STRING("FullZoomChange"),
+ true, true);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::GetFullZoom(float* aFullZoom)
+{
+ NS_ENSURE_ARG_POINTER(aFullZoom);
+#ifdef NS_PRINT_PREVIEW
+ if (GetIsPrintPreview()) {
+ *aFullZoom = mPrintPreviewZoom;
+ return NS_OK;
+ }
+#endif
+ // Check the prescontext first because it might have a temporary
+ // setting for print-preview
+ nsPresContext* pc = GetPresContext();
+ *aFullZoom = pc ? pc->GetFullZoom() : mPageZoom;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::SetOverrideDPPX(float aDPPX)
+{
+ // If we don't have a document, then we need to bail.
+ if (!mDocument) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mOverrideDPPX = aDPPX;
+
+ struct ZoomInfo ZoomInfo = { aDPPX };
+ CallChildren(SetChildOverrideDPPX, &ZoomInfo);
+
+ nsPresContext* pc = GetPresContext();
+ if (pc) {
+ pc->SetOverrideDPPX(aDPPX);
+ }
+
+ // And do the external resources
+ mDocument->EnumerateExternalResources(SetExtResourceOverrideDPPX, &ZoomInfo);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::GetOverrideDPPX(float* aDPPX)
+{
+ NS_ENSURE_ARG_POINTER(aDPPX);
+
+ nsPresContext* pc = GetPresContext();
+ *aDPPX = pc ? pc->GetOverrideDPPX() : mOverrideDPPX;
+ return NS_OK;
+}
+
+static void
+SetChildAuthorStyleDisabled(nsIContentViewer* aChild, void* aClosure)
+{
+ bool styleDisabled = *static_cast<bool*>(aClosure);
+ aChild->SetAuthorStyleDisabled(styleDisabled);
+}
+
+
+NS_IMETHODIMP
+nsDocumentViewer::SetAuthorStyleDisabled(bool aStyleDisabled)
+{
+ if (mPresShell) {
+ mPresShell->SetAuthorStyleDisabled(aStyleDisabled);
+ }
+ CallChildren(SetChildAuthorStyleDisabled, &aStyleDisabled);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::GetAuthorStyleDisabled(bool* aStyleDisabled)
+{
+ if (mPresShell) {
+ *aStyleDisabled = mPresShell->GetAuthorStyleDisabled();
+ } else {
+ *aStyleDisabled = false;
+ }
+ return NS_OK;
+}
+
+static bool
+ExtResourceEmulateMedium(nsIDocument* aDocument, void* aClosure)
+{
+ nsIPresShell* shell = aDocument->GetShell();
+ if (shell) {
+ nsPresContext* ctxt = shell->GetPresContext();
+ if (ctxt) {
+ const nsAString* mediaType = static_cast<nsAString*>(aClosure);
+ ctxt->EmulateMedium(*mediaType);
+ }
+ }
+
+ return true;
+}
+
+static void
+ChildEmulateMedium(nsIContentViewer* aChild, void* aClosure)
+{
+ const nsAString* mediaType = static_cast<nsAString*>(aClosure);
+ aChild->EmulateMedium(*mediaType);
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::EmulateMedium(const nsAString& aMediaType)
+{
+ if (mPresContext) {
+ mPresContext->EmulateMedium(aMediaType);
+ }
+ CallChildren(ChildEmulateMedium, const_cast<nsAString*>(&aMediaType));
+
+ if (mDocument) {
+ mDocument->EnumerateExternalResources(ExtResourceEmulateMedium,
+ const_cast<nsAString*>(&aMediaType));
+ }
+
+ return NS_OK;
+}
+
+static bool
+ExtResourceStopEmulatingMedium(nsIDocument* aDocument, void* aClosure)
+{
+ nsIPresShell* shell = aDocument->GetShell();
+ if (shell) {
+ nsPresContext* ctxt = shell->GetPresContext();
+ if (ctxt) {
+ ctxt->StopEmulatingMedium();
+ }
+ }
+
+ return true;
+}
+
+static void
+ChildStopEmulatingMedium(nsIContentViewer* aChild, void* aClosure)
+{
+ aChild->StopEmulatingMedium();
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::StopEmulatingMedium()
+{
+ if (mPresContext) {
+ mPresContext->StopEmulatingMedium();
+ }
+ CallChildren(ChildStopEmulatingMedium, nullptr);
+
+ if (mDocument) {
+ mDocument->EnumerateExternalResources(ExtResourceStopEmulatingMedium,
+ nullptr);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsDocumentViewer::GetForceCharacterSet(nsACString& aForceCharacterSet)
+{
+ aForceCharacterSet = mForceCharacterSet;
+ return NS_OK;
+}
+
+static void
+SetChildForceCharacterSet(nsIContentViewer* aChild, void* aClosure)
+{
+ const nsACString* charset = static_cast<nsACString*>(aClosure);
+ aChild->SetForceCharacterSet(*charset);
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::SetForceCharacterSet(const nsACString& aForceCharacterSet)
+{
+ // This method is scriptable, so add-ons could pass in something other
+ // than a canonical name. However, in case where the input is a canonical
+ // name, "replacement" doesn't survive label resolution. Additionally, the
+ // empty string means no hint.
+ nsAutoCString encoding;
+ if (!aForceCharacterSet.IsEmpty()) {
+ if (aForceCharacterSet.EqualsLiteral("replacement")) {
+ encoding.AssignLiteral("replacement");
+ } else if (!EncodingUtils::FindEncodingForLabel(aForceCharacterSet,
+ encoding)) {
+ // Reject unknown labels
+ return NS_ERROR_INVALID_ARG;
+ }
+ }
+ mForceCharacterSet = encoding;
+ // now set the force char set on all children of mContainer
+ CallChildren(SetChildForceCharacterSet, (void*) &aForceCharacterSet);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsDocumentViewer::GetHintCharacterSet(nsACString& aHintCharacterSet)
+{
+
+ if(kCharsetUninitialized == mHintCharsetSource) {
+ aHintCharacterSet.Truncate();
+ } else {
+ aHintCharacterSet = mHintCharset;
+ // this can't possibly be right. we can't set a value just because somebody got a related value!
+ //mHintCharsetSource = kCharsetUninitialized;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsDocumentViewer::GetHintCharacterSetSource(int32_t *aHintCharacterSetSource)
+{
+ NS_ENSURE_ARG_POINTER(aHintCharacterSetSource);
+
+ *aHintCharacterSetSource = mHintCharsetSource;
+ return NS_OK;
+}
+
+static void
+SetChildHintCharacterSetSource(nsIContentViewer* aChild, void* aClosure)
+{
+ aChild->SetHintCharacterSetSource(NS_PTR_TO_INT32(aClosure));
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::SetHintCharacterSetSource(int32_t aHintCharacterSetSource)
+{
+ mHintCharsetSource = aHintCharacterSetSource;
+ // now set the hint char set source on all children of mContainer
+ CallChildren(SetChildHintCharacterSetSource,
+ NS_INT32_TO_PTR(aHintCharacterSetSource));
+ return NS_OK;
+}
+
+static void
+SetChildHintCharacterSet(nsIContentViewer* aChild, void* aClosure)
+{
+ const nsACString* charset = static_cast<nsACString*>(aClosure);
+ aChild->SetHintCharacterSet(*charset);
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::SetHintCharacterSet(const nsACString& aHintCharacterSet)
+{
+ // This method is scriptable, so add-ons could pass in something other
+ // than a canonical name. However, in case where the input is a canonical
+ // name, "replacement" doesn't survive label resolution. Additionally, the
+ // empty string means no hint.
+ nsAutoCString encoding;
+ if (!aHintCharacterSet.IsEmpty()) {
+ if (aHintCharacterSet.EqualsLiteral("replacement")) {
+ encoding.AssignLiteral("replacement");
+ } else if (!EncodingUtils::FindEncodingForLabel(aHintCharacterSet,
+ encoding)) {
+ // Reject unknown labels
+ return NS_ERROR_INVALID_ARG;
+ }
+ }
+ mHintCharset = encoding;
+ // now set the hint char set on all children of mContainer
+ CallChildren(SetChildHintCharacterSet, (void*) &aHintCharacterSet);
+ return NS_OK;
+}
+
+static void
+AppendChildSubtree(nsIContentViewer* aChild, void* aClosure)
+{
+ nsTArray<nsCOMPtr<nsIContentViewer> >& array =
+ *static_cast<nsTArray<nsCOMPtr<nsIContentViewer> >*>(aClosure);
+ aChild->AppendSubtree(array);
+}
+
+NS_IMETHODIMP nsDocumentViewer::AppendSubtree(nsTArray<nsCOMPtr<nsIContentViewer> >& aArray)
+{
+ aArray.AppendElement(this);
+ CallChildren(AppendChildSubtree, &aArray);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::PausePainting()
+{
+ bool enablePaint = false;
+ CallChildren(ChangeChildPaintingEnabled, &enablePaint);
+
+ nsIPresShell* presShell = GetPresShell();
+ if (presShell) {
+ presShell->PausePainting();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::ResumePainting()
+{
+ bool enablePaint = true;
+ CallChildren(ChangeChildPaintingEnabled, &enablePaint);
+
+ nsIPresShell* presShell = GetPresShell();
+ if (presShell) {
+ presShell->ResumePainting();
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsDocumentViewer::GetContentSizeInternal(int32_t* aWidth, int32_t* aHeight,
+ nscoord aMaxWidth, nscoord aMaxHeight)
+{
+ NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
+
+ nsCOMPtr<nsIPresShell> presShell;
+ GetPresShell(getter_AddRefs(presShell));
+ 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(Flush_Layout);
+
+ nsIFrame *root = presShell->GetRootFrame();
+ NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
+
+ nscoord prefWidth;
+ {
+ nsRenderingContext rcx(presShell->CreateReferenceRenderingContext());
+ prefWidth = root->GetPrefISize(&rcx);
+ }
+ if (prefWidth > aMaxWidth) {
+ prefWidth = aMaxWidth;
+ }
+
+ nsresult rv = presShell->ResizeReflow(prefWidth, NS_UNCONSTRAINEDSIZE);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<nsPresContext> presContext;
+ GetPresContext(getter_AddRefs(presContext));
+ NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
+
+ // so how big is it?
+ nsRect shellArea = presContext->GetVisibleArea();
+ if (shellArea.height > aMaxHeight) {
+ // Reflow to max height if we would up too tall.
+ rv = presShell->ResizeReflow(prefWidth, aMaxHeight);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ shellArea = presContext->GetVisibleArea();
+ }
+
+ // Protect against bogus returns here
+ NS_ENSURE_TRUE(shellArea.width != NS_UNCONSTRAINEDSIZE &&
+ shellArea.height != NS_UNCONSTRAINEDSIZE,
+ NS_ERROR_FAILURE);
+
+ *aWidth = presContext->AppUnitsToDevPixels(shellArea.width);
+ *aHeight = presContext->AppUnitsToDevPixels(shellArea.height);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::GetContentSize(int32_t* aWidth, int32_t* aHeight)
+{
+ // Skip doing this on docshell-less documents for now
+ nsCOMPtr<nsIDocShellTreeItem> docShellAsItem(mContainer);
+ NS_ENSURE_TRUE(docShellAsItem, NS_ERROR_NOT_AVAILABLE);
+
+ nsCOMPtr<nsIDocShellTreeItem> docShellParent;
+ docShellAsItem->GetSameTypeParent(getter_AddRefs(docShellParent));
+
+ // It's only valid to access this from a top frame. Doesn't work from
+ // sub-frames.
+ NS_ENSURE_TRUE(!docShellParent, NS_ERROR_FAILURE);
+
+ return GetContentSizeInternal(aWidth, aHeight, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::GetContentSizeConstrained(int32_t aMaxWidth, int32_t aMaxHeight,
+ int32_t* aWidth, int32_t* aHeight)
+{
+ RefPtr<nsPresContext> presContext;
+ GetPresContext(getter_AddRefs(presContext));
+ NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
+
+ nscoord maxWidth = NS_UNCONSTRAINEDSIZE;
+ nscoord maxHeight = NS_UNCONSTRAINEDSIZE;
+ if (aMaxWidth > 0) {
+ maxWidth = presContext->DevPixelsToAppUnits(aMaxWidth);
+ }
+ if (aMaxHeight > 0) {
+ maxHeight = presContext->DevPixelsToAppUnits(aMaxHeight);
+ }
+
+ return GetContentSizeInternal(aWidth, aHeight, maxWidth, maxHeight);
+}
+
+
+NS_IMPL_ISUPPORTS(nsDocViewerSelectionListener, nsISelectionListener)
+
+nsresult nsDocViewerSelectionListener::Init(nsDocumentViewer *aDocViewer)
+{
+ mDocViewer = aDocViewer;
+ return NS_OK;
+}
+
+/*
+ * 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.
+ */
+
+nsresult
+nsDocumentViewer::GetPopupNode(nsIDOMNode** aNode)
+{
+ NS_ENSURE_ARG_POINTER(aNode);
+
+ *aNode = nullptr;
+
+ // get the document
+ nsIDocument* document = GetDocument();
+ NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
+
+ // get the private dom window
+ nsCOMPtr<nsPIDOMWindowOuter> window(document->GetWindow());
+ NS_ENSURE_TRUE(window, NS_ERROR_NOT_AVAILABLE);
+ if (window) {
+ nsCOMPtr<nsPIWindowRoot> root = window->GetTopWindowRoot();
+ NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
+
+ // get the popup node
+ nsCOMPtr<nsIDOMNode> node = root->GetPopupNode();
+#ifdef MOZ_XUL
+ if (!node) {
+ nsPIDOMWindowOuter* rootWindow = root->GetWindow();
+ if (rootWindow) {
+ nsCOMPtr<nsIDocument> rootDoc = rootWindow->GetExtantDoc();
+ if (rootDoc) {
+ nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
+ if (pm) {
+ node = pm->GetLastTriggerPopupNode(rootDoc);
+ }
+ }
+ }
+ }
+#endif
+ node.swap(*aNode);
+ }
+
+ return NS_OK;
+}
+
+// GetPopupLinkNode: return popup link node or fail
+nsresult
+nsDocumentViewer::GetPopupLinkNode(nsIDOMNode** aNode)
+{
+ NS_ENSURE_ARG_POINTER(aNode);
+
+ // you get null unless i say so
+ *aNode = nullptr;
+
+ // find popup node
+ nsCOMPtr<nsIDOMNode> node;
+ nsresult rv = GetPopupNode(getter_AddRefs(node));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // find out if we have a link in our ancestry
+ while (node) {
+
+ nsCOMPtr<nsIContent> content(do_QueryInterface(node));
+ if (content) {
+ nsCOMPtr<nsIURI> hrefURI = content->GetHrefURI();
+ if (hrefURI) {
+ *aNode = node;
+ NS_IF_ADDREF(*aNode); // addref
+ return NS_OK;
+ }
+ }
+
+ // get our parent and keep trying...
+ nsCOMPtr<nsIDOMNode> parentNode;
+ node->GetParentNode(getter_AddRefs(parentNode));
+ node = parentNode;
+ }
+
+ // if we have no node, fail
+ return NS_ERROR_FAILURE;
+}
+
+// GetPopupLinkNode: return popup image node or fail
+nsresult
+nsDocumentViewer::GetPopupImageNode(nsIImageLoadingContent** aNode)
+{
+ NS_ENSURE_ARG_POINTER(aNode);
+
+ // you get null unless i say so
+ *aNode = nullptr;
+
+ // find popup node
+ nsCOMPtr<nsIDOMNode> node;
+ nsresult rv = GetPopupNode(getter_AddRefs(node));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (node)
+ CallQueryInterface(node, aNode);
+
+ return NS_OK;
+}
+
+/*
+ * 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<nsIDOMNode> node;
+ nsresult rv = GetPopupLinkNode(getter_AddRefs(node));
+ if (NS_FAILED(rv)) return rv;
+ NS_ENSURE_TRUE(node, 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;
+ nsresult rv = GetPopupImageNode(getter_AddRefs(node));
+ if (NS_FAILED(rv)) return rv;
+ 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(nsIDOMDocument *, nsISelection *, int16_t aReason)
+{
+ if (!mDocViewer) {
+ return NS_OK;
+ }
+
+ // get the selection state
+ RefPtr<mozilla::dom::Selection> selection = mDocViewer->GetDocumentSelection();
+ if (!selection) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsIDocument* theDoc = mDocViewer->GetDocument();
+ if (!theDoc) return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsPIDOMWindowOuter> domWindow = theDoc->GetWindow();
+ if (!domWindow) return NS_ERROR_FAILURE;
+
+ bool selectionCollapsed;
+ selection->GetIsCollapsed(&selectionCollapsed);
+ // 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(NS_LITERAL_STRING("select"), selection, aReason);
+ mSelectionWasCollapsed = selectionCollapsed;
+ }
+
+ return NS_OK;
+}
+
+//nsDocViewerFocusListener
+NS_IMPL_ISUPPORTS(nsDocViewerFocusListener,
+ nsIDOMEventListener)
+
+nsDocViewerFocusListener::nsDocViewerFocusListener()
+:mDocViewer(nullptr)
+{
+}
+
+nsDocViewerFocusListener::~nsDocViewerFocusListener(){}
+
+nsresult
+nsDocViewerFocusListener::HandleEvent(nsIDOMEvent* aEvent)
+{
+ NS_ENSURE_STATE(mDocViewer);
+
+ nsCOMPtr<nsIPresShell> shell;
+ mDocViewer->GetPresShell(getter_AddRefs(shell));
+ NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsISelectionController> selCon = do_QueryInterface(shell);
+ int16_t selectionStatus;
+ selCon->GetDisplaySelection(&selectionStatus);
+
+ 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) {
+ selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
+ selCon->RepaintSelection(nsISelectionController::SELECTION_NORMAL);
+ }
+ } else {
+ MOZ_ASSERT(eventType.EqualsLiteral("blur"), "Unexpected event type");
+ // If selection was on, disable it.
+ if(selectionStatus == nsISelectionController::SELECTION_ON ||
+ selectionStatus == nsISelectionController::SELECTION_ATTENTION) {
+ selCon->SetDisplaySelection(nsISelectionController::SELECTION_DISABLED);
+ selCon->RepaintSelection(nsISelectionController::SELECTION_NORMAL);
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsDocViewerFocusListener::Init(nsDocumentViewer *aDocViewer)
+{
+ mDocViewer = aDocViewer;
+ return NS_OK;
+}
+
+/** ---------------------------------------------------
+ * From nsIWebBrowserPrint
+ */
+
+#ifdef NS_PRINTING
+
+NS_IMETHODIMP
+nsDocumentViewer::Print(nsIPrintSettings* aPrintSettings,
+ nsIWebProgressListener* aWebProgressListener)
+{
+ // Printing XUL documents is not supported.
+ nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocument));
+ if (xulDoc) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!mContainer) {
+ PR_PL(("Container was destroyed yet we are still trying to use it!"));
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIDocShell> docShell(mContainer);
+ NS_ENSURE_STATE(docShell);
+
+ // Check to see if this document is still busy
+ // If it is busy and we aren't already "queued" up to print then
+ // Indicate there is a print pending and cache the args for later
+ uint32_t busyFlags = nsIDocShell::BUSY_FLAGS_NONE;
+ if ((NS_FAILED(docShell->GetBusyFlags(&busyFlags)) ||
+ (busyFlags != nsIDocShell::BUSY_FLAGS_NONE && busyFlags & nsIDocShell::BUSY_FLAGS_PAGE_LOADING)) &&
+ !mPrintDocIsFullyLoaded) {
+ if (!mPrintIsPending) {
+ mCachedPrintSettings = aPrintSettings;
+ mCachedPrintWebProgressListner = aWebProgressListener;
+ mPrintIsPending = true;
+ }
+ PR_PL(("Printing Stopped - document is still busy!"));
+ return NS_ERROR_GFX_PRINTER_DOC_IS_BUSY;
+ }
+
+ if (!mDocument || !mDeviceContext) {
+ PR_PL(("Can't Print without a document and a device context"));
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv;
+
+ // 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
+ if (GetIsPrinting()) {
+ // Let the user know we are not ready to print.
+ rv = NS_ERROR_NOT_AVAILABLE;
+
+ if (mPrintEngine) {
+ mPrintEngine->FirePrintingErrorEvent(rv);
+ }
+
+ return rv;
+ }
+
+ // Dispatch 'beforeprint' event and ensure 'afterprint' will be dispatched:
+ MOZ_ASSERT(!mAutoBeforeAndAfterPrint,
+ "We don't want to dispatch nested beforeprint/afterprint");
+ nsAutoPtr<AutoPrintEventDispatcher> autoBeforeAndAfterPrint(
+ new AutoPrintEventDispatcher(mDocument));
+ NS_ENSURE_STATE(!GetIsPrinting());
+ // If we are hosting a full-page plugin, tell it to print
+ // first. It shows its own native print UI.
+ nsCOMPtr<nsIPluginDocument> pDoc(do_QueryInterface(mDocument));
+ if (pDoc)
+ return pDoc->Print();
+
+ if (!mPrintEngine) {
+ NS_ENSURE_STATE(mDeviceContext);
+ mPrintEngine = new nsPrintEngine();
+
+ rv = mPrintEngine->Initialize(this, mContainer, mDocument,
+ float(mDeviceContext->AppUnitsPerCSSInch()) /
+ float(mDeviceContext->AppUnitsPerDevPixel()) /
+ mPageZoom,
+#ifdef DEBUG
+ mDebugFile
+#else
+ nullptr
+#endif
+ );
+ if (NS_FAILED(rv)) {
+ mPrintEngine->Destroy();
+ mPrintEngine = nullptr;
+ return rv;
+ }
+ }
+ if (mPrintEngine->HasPrintCallbackCanvas()) {
+ // Postpone the 'afterprint' event until after the mozPrintCallback
+ // callbacks have been called:
+ mAutoBeforeAndAfterPrint = autoBeforeAndAfterPrint;
+ }
+ dom::Element* root = mDocument->GetRootElement();
+ if (root && root->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdisallowselectionprint)) {
+ mPrintEngine->SetDisallowSelectionPrint(true);
+ }
+ rv = mPrintEngine->Print(aPrintSettings, aWebProgressListener);
+ if (NS_FAILED(rv)) {
+ OnDonePrinting();
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::PrintPreview(nsIPrintSettings* aPrintSettings,
+ mozIDOMWindowProxy* aChildDOMWin,
+ nsIWebProgressListener* aWebProgressListener)
+{
+#if defined(NS_PRINTING) && defined(NS_PRINT_PREVIEW)
+ NS_WARNING_ASSERTION(
+ IsInitializedForPrintPreview(),
+ "Using docshell.printPreview is the preferred way for print previewing!");
+
+ NS_ENSURE_ARG_POINTER(aChildDOMWin);
+ nsresult rv = NS_OK;
+
+ if (GetIsPrinting()) {
+ nsPrintEngine::CloseProgressDialog(aWebProgressListener);
+ return NS_ERROR_FAILURE;
+ }
+
+ // Printing XUL documents is not supported.
+ nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocument));
+ if (xulDoc) {
+ nsPrintEngine::CloseProgressDialog(aWebProgressListener);
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIDocShell> docShell(mContainer);
+ if (!docShell || !mDeviceContext) {
+ PR_PL(("Can't Print Preview without device context and docshell"));
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryInterface(aChildDOMWin);
+ MOZ_ASSERT(window);
+ nsCOMPtr<nsIDocument> doc = window->GetDoc();
+ NS_ENSURE_STATE(doc);
+
+ // Dispatch 'beforeprint' event and ensure 'afterprint' will be dispatched:
+ // XXX Currently[1] when the user switches between portrait and landscape
+ // mode in print preview, we re-enter this function before
+ // mAutoBeforeAndAfterPrint (if set) is cleared to dispatch the 'afterprint'
+ // event. To avoid sending multiple 'beforeprint'/'afterprint' events we
+ // must avoid creating a new AutoPrintEventDispatcher object here if we
+ // already have one saved in mAutoBeforeAndAfterPrint.
+ // [1] Until PDF.js is removed (though, maybe after that as well).
+ nsAutoPtr<AutoPrintEventDispatcher> autoBeforeAndAfterPrint;
+ if (!mAutoBeforeAndAfterPrint) {
+ autoBeforeAndAfterPrint = new AutoPrintEventDispatcher(doc);
+ }
+ NS_ENSURE_STATE(!GetIsPrinting());
+ // beforeprint event may have caused ContentViewer to be shutdown.
+ NS_ENSURE_STATE(mContainer);
+ NS_ENSURE_STATE(mDeviceContext);
+ if (!mPrintEngine) {
+ mPrintEngine = new nsPrintEngine();
+
+ rv = mPrintEngine->Initialize(this, mContainer, doc,
+ float(mDeviceContext->AppUnitsPerCSSInch()) /
+ float(mDeviceContext->AppUnitsPerDevPixel()) /
+ mPageZoom,
+#ifdef DEBUG
+ mDebugFile
+#else
+ nullptr
+#endif
+ );
+ if (NS_FAILED(rv)) {
+ mPrintEngine->Destroy();
+ mPrintEngine = nullptr;
+ return rv;
+ }
+ }
+ if (autoBeforeAndAfterPrint &&
+ mPrintEngine->HasPrintCallbackCanvas()) {
+ // Postpone the 'afterprint' event until after the mozPrintCallback
+ // callbacks have been called:
+ mAutoBeforeAndAfterPrint = autoBeforeAndAfterPrint;
+ }
+ dom::Element* root = doc->GetRootElement();
+ if (root && root->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdisallowselectionprint)) {
+ PR_PL(("PrintPreview: found mozdisallowselectionprint"));
+ mPrintEngine->SetDisallowSelectionPrint(true);
+ }
+ rv = mPrintEngine->PrintPreview(aPrintSettings, aChildDOMWin, aWebProgressListener);
+ mPrintPreviewZoomed = false;
+ if (NS_FAILED(rv)) {
+ OnDonePrinting();
+ }
+ return rv;
+#else
+ return NS_ERROR_FAILURE;
+#endif
+}
+
+//----------------------------------------------------------------------
+NS_IMETHODIMP
+nsDocumentViewer::PrintPreviewNavigate(int16_t aType, int32_t aPageNum)
+{
+ if (!GetIsPrintPreview() ||
+ mPrintEngine->GetIsCreatingPrintPreview())
+ return NS_ERROR_FAILURE;
+
+ nsIScrollableFrame* sf =
+ mPrintEngine->GetPrintPreviewPresShell()->GetRootScrollFrameAsScrollable();
+ if (!sf)
+ return NS_OK;
+
+ // Check to see if we can short circut scrolling to the top
+ if (aType == nsIWebBrowserPrint::PRINTPREVIEW_HOME ||
+ (aType == nsIWebBrowserPrint::PRINTPREVIEW_GOTO_PAGENUM && aPageNum == 1)) {
+ sf->ScrollTo(nsPoint(0, 0), nsIScrollableFrame::INSTANT);
+ return NS_OK;
+ }
+
+ // Finds the SimplePageSequencer frame
+ // in PP mPrtPreview->mPrintObject->mSeqFrame is null
+ nsIFrame* seqFrame = nullptr;
+ int32_t pageCount = 0;
+ if (NS_FAILED(mPrintEngine->GetSeqFrameAndCountPages(seqFrame, pageCount))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Figure where we are currently scrolled to
+ nsPoint pt = sf->GetScrollPosition();
+
+ int32_t pageNum = 1;
+ nsIFrame * fndPageFrame = nullptr;
+ nsIFrame * currentPage = nullptr;
+
+ // If it is "End" then just do a "goto" to the last page
+ if (aType == nsIWebBrowserPrint::PRINTPREVIEW_END) {
+ aType = nsIWebBrowserPrint::PRINTPREVIEW_GOTO_PAGENUM;
+ aPageNum = pageCount;
+ }
+
+ // Now, locate the current page we are on and
+ // and the page of the page number
+ for (nsIFrame* pageFrame : seqFrame->PrincipalChildList()) {
+ nsRect pageRect = pageFrame->GetRect();
+ if (pageRect.Contains(pageRect.x, pt.y)) {
+ currentPage = pageFrame;
+ }
+ if (pageNum == aPageNum) {
+ fndPageFrame = pageFrame;
+ break;
+ }
+ pageNum++;
+ }
+
+ if (aType == nsIWebBrowserPrint::PRINTPREVIEW_PREV_PAGE) {
+ if (currentPage) {
+ fndPageFrame = currentPage->GetPrevInFlow();
+ if (!fndPageFrame) {
+ return NS_OK;
+ }
+ } else {
+ return NS_OK;
+ }
+ } else if (aType == nsIWebBrowserPrint::PRINTPREVIEW_NEXT_PAGE) {
+ if (currentPage) {
+ fndPageFrame = currentPage->GetNextInFlow();
+ if (!fndPageFrame) {
+ return NS_OK;
+ }
+ } else {
+ return NS_OK;
+ }
+ } else { // If we get here we are doing "GoTo"
+ if (aPageNum < 0 || aPageNum > pageCount) {
+ return NS_OK;
+ }
+ }
+
+ if (fndPageFrame) {
+ nscoord newYPosn =
+ nscoord(mPrintEngine->GetPrintPreviewScale() * fndPageFrame->GetPosition().y);
+ sf->ScrollTo(nsPoint(pt.x, newYPosn), nsIScrollableFrame::INSTANT);
+ }
+ return NS_OK;
+
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::GetGlobalPrintSettings(nsIPrintSettings * *aGlobalPrintSettings)
+{
+ return nsPrintEngine::GetGlobalPrintSettings(aGlobalPrintSettings);
+}
+
+// XXX This always returns false for subdocuments
+NS_IMETHODIMP
+nsDocumentViewer::GetDoingPrint(bool *aDoingPrint)
+{
+ NS_ENSURE_ARG_POINTER(aDoingPrint);
+
+ *aDoingPrint = false;
+ if (mPrintEngine) {
+ // XXX shouldn't this be GetDoingPrint() ?
+ return mPrintEngine->GetDoingPrintPreview(aDoingPrint);
+ }
+ return NS_OK;
+}
+
+// XXX This always returns false for subdocuments
+NS_IMETHODIMP
+nsDocumentViewer::GetDoingPrintPreview(bool *aDoingPrintPreview)
+{
+ NS_ENSURE_ARG_POINTER(aDoingPrintPreview);
+
+ *aDoingPrintPreview = false;
+ if (mPrintEngine) {
+ return mPrintEngine->GetDoingPrintPreview(aDoingPrintPreview);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::GetCurrentPrintSettings(nsIPrintSettings * *aCurrentPrintSettings)
+{
+ NS_ENSURE_ARG_POINTER(aCurrentPrintSettings);
+
+ *aCurrentPrintSettings = nullptr;
+ NS_ENSURE_TRUE(mPrintEngine, NS_ERROR_FAILURE);
+
+ return mPrintEngine->GetCurrentPrintSettings(aCurrentPrintSettings);
+}
+
+
+NS_IMETHODIMP
+nsDocumentViewer::GetCurrentChildDOMWindow(mozIDOMWindowProxy** aCurrentChildDOMWindow)
+{
+ NS_ENSURE_ARG_POINTER(aCurrentChildDOMWindow);
+ *aCurrentChildDOMWindow = nullptr;
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::Cancel()
+{
+ NS_ENSURE_TRUE(mPrintEngine, NS_ERROR_FAILURE);
+ return mPrintEngine->Cancelled();
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::ExitPrintPreview()
+{
+ if (GetIsPrinting())
+ return NS_ERROR_FAILURE;
+ NS_ENSURE_TRUE(mPrintEngine, NS_ERROR_FAILURE);
+
+ if (GetIsPrintPreview()) {
+ ReturnToGalleyPresentation();
+ }
+ return NS_OK;
+}
+
+//----------------------------------------------------------------------------------
+// Enumerate all the documents for their titles
+NS_IMETHODIMP
+nsDocumentViewer::EnumerateDocumentNames(uint32_t* aCount,
+ char16_t*** aResult)
+{
+#ifdef NS_PRINTING
+ NS_ENSURE_ARG(aCount);
+ NS_ENSURE_ARG_POINTER(aResult);
+ NS_ENSURE_TRUE(mPrintEngine, NS_ERROR_FAILURE);
+
+ return mPrintEngine->EnumerateDocumentNames(aCount, aResult);
+#else
+ return NS_ERROR_FAILURE;
+#endif
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::GetIsFramesetFrameSelected(bool *aIsFramesetFrameSelected)
+{
+#ifdef NS_PRINTING
+ *aIsFramesetFrameSelected = false;
+ NS_ENSURE_TRUE(mPrintEngine, NS_ERROR_FAILURE);
+
+ return mPrintEngine->GetIsFramesetFrameSelected(aIsFramesetFrameSelected);
+#else
+ return NS_ERROR_FAILURE;
+#endif
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::GetPrintPreviewNumPages(int32_t *aPrintPreviewNumPages)
+{
+#ifdef NS_PRINTING
+ NS_ENSURE_ARG_POINTER(aPrintPreviewNumPages);
+ NS_ENSURE_TRUE(mPrintEngine, NS_ERROR_FAILURE);
+
+ return mPrintEngine->GetPrintPreviewNumPages(aPrintPreviewNumPages);
+#else
+ return NS_ERROR_FAILURE;
+#endif
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::GetIsFramesetDocument(bool *aIsFramesetDocument)
+{
+#ifdef NS_PRINTING
+ *aIsFramesetDocument = false;
+ NS_ENSURE_TRUE(mPrintEngine, NS_ERROR_FAILURE);
+
+ return mPrintEngine->GetIsFramesetDocument(aIsFramesetDocument);
+#else
+ return NS_ERROR_FAILURE;
+#endif
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::GetIsIFrameSelected(bool *aIsIFrameSelected)
+{
+#ifdef NS_PRINTING
+ *aIsIFrameSelected = false;
+ NS_ENSURE_TRUE(mPrintEngine, NS_ERROR_FAILURE);
+
+ return mPrintEngine->GetIsIFrameSelected(aIsIFrameSelected);
+#else
+ return NS_ERROR_FAILURE;
+#endif
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::GetIsRangeSelection(bool *aIsRangeSelection)
+{
+#ifdef NS_PRINTING
+ *aIsRangeSelection = false;
+ NS_ENSURE_TRUE(mPrintEngine, NS_ERROR_FAILURE);
+
+ return mPrintEngine->GetIsRangeSelection(aIsRangeSelection);
+#else
+ return NS_ERROR_FAILURE;
+#endif
+}
+
+//----------------------------------------------------------------------------------
+// Printing/Print Preview Helpers
+//----------------------------------------------------------------------------------
+
+//----------------------------------------------------------------------------------
+// Walks the document tree and tells each DocShell whether Printing/PP is happening
+void
+nsDocumentViewer::SetIsPrintingInDocShellTree(nsIDocShellTreeItem* aParentNode,
+ bool aIsPrintingOrPP,
+ bool aStartAtTop)
+{
+ nsCOMPtr<nsIDocShellTreeItem> parentItem(do_QueryInterface(aParentNode));
+
+ // find top of "same parent" tree
+ if (aStartAtTop) {
+ if (aIsPrintingOrPP) {
+ while (parentItem) {
+ nsCOMPtr<nsIDocShellTreeItem> parent;
+ parentItem->GetSameTypeParent(getter_AddRefs(parent));
+ if (!parent) {
+ break;
+ }
+ parentItem = do_QueryInterface(parent);
+ }
+ mTopContainerWhilePrinting = do_GetWeakReference(parentItem);
+ } else {
+ parentItem = do_QueryReferent(mTopContainerWhilePrinting);
+ }
+ }
+
+ // Check to see if the DocShell's ContentViewer is printing/PP
+ nsCOMPtr<nsIContentViewerContainer> viewerContainer(do_QueryInterface(parentItem));
+ if (viewerContainer) {
+ viewerContainer->SetIsPrinting(aIsPrintingOrPP);
+ }
+
+ if (!aParentNode) {
+ return;
+ }
+
+ // Traverse children to see if any of them are printing.
+ int32_t n;
+ aParentNode->GetChildCount(&n);
+ for (int32_t i=0; i < n; i++) {
+ nsCOMPtr<nsIDocShellTreeItem> child;
+ aParentNode->GetChildAt(i, getter_AddRefs(child));
+ NS_ASSERTION(child, "child isn't nsIDocShell");
+ if (child) {
+ SetIsPrintingInDocShellTree(child, aIsPrintingOrPP, false);
+ }
+ }
+
+}
+#endif // NS_PRINTING
+
+bool
+nsDocumentViewer::ShouldAttachToTopLevel()
+{
+ if (!mParentWidget)
+ return false;
+
+ nsCOMPtr<nsIDocShellTreeItem> containerItem(mContainer);
+ if (!containerItem)
+ 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)
+ // On windows, in the parent process we also attach, but just to
+ // chrome items
+ nsWindowType winType = mParentWidget->WindowType();
+ if ((winType == eWindowType_toplevel ||
+ winType == eWindowType_dialog ||
+ winType == eWindowType_invisible) &&
+ containerItem->ItemType() == nsIDocShellTreeItem::typeChrome) {
+ return true;
+ }
+#endif
+
+ return false;
+}
+
+//------------------------------------------------------------
+// XXX this always returns false for subdocuments
+bool
+nsDocumentViewer::GetIsPrinting()
+{
+#ifdef NS_PRINTING
+ if (mPrintEngine) {
+ return mPrintEngine->GetIsPrinting();
+ }
+#endif
+ return false;
+}
+
+//------------------------------------------------------------
+// Notification from the PrintEngine of the current Printing status
+void
+nsDocumentViewer::SetIsPrinting(bool aIsPrinting)
+{
+#ifdef NS_PRINTING
+ // Set all the docShells in the docshell tree to be printing.
+ // that way if anyone of them tries to "navigate" it can't
+ nsCOMPtr<nsIDocShell> docShell(mContainer);
+ if (docShell || !aIsPrinting) {
+ SetIsPrintingInDocShellTree(docShell, aIsPrinting, true);
+ } else {
+ NS_WARNING("Did you close a window before printing?");
+ }
+
+ if (!aIsPrinting) {
+ // Dispatch the 'afterprint' event now, if pending:
+ mAutoBeforeAndAfterPrint = nullptr;
+ }
+#endif
+}
+
+//------------------------------------------------------------
+// The PrintEngine holds the current value
+// this called from inside the DocViewer.
+// XXX it always returns false for subdocuments
+bool
+nsDocumentViewer::GetIsPrintPreview()
+{
+#ifdef NS_PRINTING
+ if (mPrintEngine) {
+ return mPrintEngine->GetIsPrintPreview();
+ }
+#endif
+ return false;
+}
+
+//------------------------------------------------------------
+// Notification from the PrintEngine of the current PP status
+void
+nsDocumentViewer::SetIsPrintPreview(bool aIsPrintPreview)
+{
+#ifdef NS_PRINTING
+ // Set all the docShells in the docshell tree to be printing.
+ // that way if anyone of them tries to "navigate" it can't
+ nsCOMPtr<nsIDocShell> docShell(mContainer);
+ if (docShell || !aIsPrintPreview) {
+ SetIsPrintingInDocShellTree(docShell, aIsPrintPreview, true);
+ }
+ if (!aIsPrintPreview) {
+ // Dispatch the 'afterprint' event now, if pending:
+ mAutoBeforeAndAfterPrint = nullptr;
+ }
+#endif
+
+ // Protect against pres shell destruction running scripts.
+ nsAutoScriptBlocker scriptBlocker;
+
+ if (!aIsPrintPreview) {
+ if (mPresShell) {
+ DestroyPresShell();
+ }
+ mWindow = nullptr;
+ mViewManager = nullptr;
+ mPresContext = nullptr;
+ mPresShell = nullptr;
+ }
+}
+
+//----------------------------------------------------------------------------------
+// nsIDocumentViewerPrint IFace
+//----------------------------------------------------------------------------------
+
+//------------------------------------------------------------
+void
+nsDocumentViewer::IncrementDestroyRefCount()
+{
+ ++mDestroyRefCount;
+}
+
+//------------------------------------------------------------
+
+#if defined(NS_PRINTING) && defined(NS_PRINT_PREVIEW)
+//------------------------------------------------------------
+// Reset ESM focus for all descendent doc shells.
+static void
+ResetFocusState(nsIDocShell* aDocShell)
+{
+ nsIFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (!fm)
+ return;
+
+ nsCOMPtr<nsISimpleEnumerator> docShellEnumerator;
+ aDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeContent,
+ nsIDocShell::ENUMERATE_FORWARDS,
+ getter_AddRefs(docShellEnumerator));
+
+ nsCOMPtr<nsISupports> currentContainer;
+ bool hasMoreDocShells;
+ while (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMoreDocShells))
+ && hasMoreDocShells) {
+ docShellEnumerator->GetNext(getter_AddRefs(currentContainer));
+ nsCOMPtr<nsPIDOMWindowOuter> win = do_GetInterface(currentContainer);
+ if (win)
+ fm->ClearFocus(win);
+ }
+}
+#endif // NS_PRINTING && NS_PRINT_PREVIEW
+
+void
+nsDocumentViewer::ReturnToGalleyPresentation()
+{
+#if defined(NS_PRINTING) && defined(NS_PRINT_PREVIEW)
+ if (!GetIsPrintPreview()) {
+ NS_ERROR("Wow, we should never get here!");
+ return;
+ }
+
+ SetIsPrintPreview(false);
+
+ mPrintEngine->TurnScriptingOn(true);
+ mPrintEngine->Destroy();
+ mPrintEngine = nullptr;
+
+ nsCOMPtr<nsIDocShell> docShell(mContainer);
+ ResetFocusState(docShell);
+
+ SetTextZoom(mTextZoom);
+ SetFullZoom(mPageZoom);
+ SetOverrideDPPX(mOverrideDPPX);
+ SetMinFontSize(mMinFontSize);
+ Show();
+
+#endif // NS_PRINTING && NS_PRINT_PREVIEW
+}
+
+//------------------------------------------------------------
+// This called ONLY when printing has completed and the DV
+// is being notified that it should get rid of the PrintEngine.
+//
+// BUT, if we are in Print Preview then we want to ignore the
+// notification (we do not get rid of the PrintEngine)
+//
+// 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 (mPrintEngine) {
+ RefPtr<nsPrintEngine> pe = mPrintEngine;
+ if (GetIsPrintPreview()) {
+ pe->DestroyPrintingData();
+ } else {
+ mPrintEngine = nullptr;
+ pe->Destroy();
+ }
+
+ // We are done printing, now cleanup
+ if (mDeferredWindowClose) {
+ mDeferredWindowClose = false;
+ if (mContainer) {
+ if (nsCOMPtr<nsPIDOMWindowOuter> win = do_QueryInterface(mContainer->GetWindow())) {
+ win->Close();
+ }
+ }
+ } else if (mClosingWhilePrinting) {
+ if (mDocument) {
+ mDocument->Destroy();
+ mDocument = nullptr;
+ }
+ mClosingWhilePrinting = false;
+ }
+ }
+#endif // NS_PRINTING && NS_PRINT_PREVIEW
+}
+
+NS_IMETHODIMP nsDocumentViewer::SetPageMode(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, mBounds, true, 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::DestroyPresContext()
+{
+ mPresContext->Detach();
+ mPresContext = nullptr;
+}
+
+bool
+nsDocumentViewer::IsInitializedForPrintPreview()
+{
+ return mInitializedForPrintPreview;
+}
+
+void
+nsDocumentViewer::InitializeForPrintPreview()
+{
+ mInitializedForPrintPreview = true;
+}
+
+void
+nsDocumentViewer::SetPrintPreviewPresentation(nsViewManager* aViewManager,
+ nsPresContext* aPresContext,
+ nsIPresShell* 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(mDocument, "document-shown", nullptr);
+ }
+ return NS_OK;
+}
+