diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index b088e0a37250..c87701501c5c 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -5986,10 +5986,6 @@ nsBrowserAccess.prototype = { ); }, - print(aBrowsingContext) { - PrintUtils.startPrintWindow(aBrowsingContext); - }, - openURI(aURI, aOpenWindowInfo, aWhere, aFlags, aTriggeringPrincipal, aCsp) { if (!aURI) { Cu.reportError("openURI should only be called with a valid URI"); @@ -6107,7 +6103,7 @@ nsBrowserAccess.prototype = { Cu.reportError(ex); } break; - case Ci.nsIBrowserDOMWindow.OPEN_NEWTAB: + case Ci.nsIBrowserDOMWindow.OPEN_NEWTAB: { // If we have an opener, that means that the caller is expecting access // to the nsIDOMWindow of the opened tab right away. For e10s windows, // this means forcing the newly opened browser to be non-remote so that @@ -6136,6 +6132,17 @@ nsBrowserAccess.prototype = { browsingContext = browser.browsingContext; } break; + } + case Ci.nsIBrowserDOMWindow.OPEN_PRINT_BROWSER: { + let browser = PrintUtils.startPrintWindow( + aOpenWindowInfo.parent, + aOpenWindowInfo + ); + if (browser) { + browsingContext = browser.browsingContext; + } + break; + } default: // OPEN_CURRENTWINDOW or an illegal value browsingContext = window.gBrowser.selectedBrowser.browsingContext; @@ -6209,8 +6216,15 @@ nsBrowserAccess.prototype = { aName, aSkipLoad ) { + if (aWhere == Ci.nsIBrowserDOMWindow.OPEN_PRINT_BROWSER) { + return PrintUtils.startPrintWindow( + aParams.openWindowInfo.parent, + aParams.openWindowInfo + ); + } + if (aWhere != Ci.nsIBrowserDOMWindow.OPEN_NEWTAB) { - dump("Error: openURIInFrame can only open in new tabs"); + dump("Error: openURIInFrame can only open in new tabs or print"); return null; } diff --git a/browser/components/ssb/content/ssb.js b/browser/components/ssb/content/ssb.js index f2fa46938638..92549b86a61a 100644 --- a/browser/components/ssb/content/ssb.js +++ b/browser/components/ssb/content/ssb.js @@ -144,10 +144,6 @@ class BrowserDOMWindow { throw Components.Exception("", Cr.NS_ERROR_FAILURE); } - print(aBrowsingContext) { - PrintUtils.startPrintWindow(aBrowsingContext); - } - /** * Finds a new frame to load some content in. * @@ -170,6 +166,12 @@ class BrowserDOMWindow { // It's been determined that this load needs to happen in a new frame. // Either onBeforeLinkTraversal set this correctly or this is the result // of a window.open call. + if (where == Ci.nsIBrowserDOMWindow.OPEN_PRINT_BROWSER) { + return PrintUtils.startPrintWindow( + params.openWindowInfo.parent, + params.openWindowInfo + ); + } // If this ssb can load the url then just load it internally. if (gSSB.canLoad(uri)) { diff --git a/docshell/base/BrowsingContext.cpp b/docshell/base/BrowsingContext.cpp index 97046b872c2d..77a05eaed492 100644 --- a/docshell/base/BrowsingContext.cpp +++ b/docshell/base/BrowsingContext.cpp @@ -2033,60 +2033,6 @@ void BrowsingContext::DidSet(FieldIndex, bool aOldValue) { } } -// We only care about whether we transition from not-awaiting to waiting. In -// that case, we actually want to trigger the print process in the parent -// process (which eventually will toggle this back to false, stopping the -// event-loop-spinning going on in nsGlobalWindowOuter::PrintOuter). -void BrowsingContext::DidSet(FieldIndex, bool aOldValue) { - if (!GetIsAwaitingPrint() || aOldValue == GetIsAwaitingPrint()) { - return; - } - - if (!XRE_IsParentProcess()) { - return; - } - - auto unsetFlagOnFailure = MakeScopeExit([&] { - // If we hit this, the print process failed, but let's not leave the event - // loop spinning on the child. - // - // If it fails because of the browsing context being discarded, then that's - // fine as well, the child event loop will also stop spinning. - Unused << SetIsAwaitingPrint(false); - }); - - RefPtr el = Top()->GetEmbedderElement(); - if (NS_WARN_IF(!el)) { - return; - } - - nsCOMPtr outerWin = el->OwnerDoc()->GetWindow(); - if (NS_WARN_IF(!outerWin)) { - return; - } - - nsCOMPtr chromeWin = do_QueryInterface(outerWin); - if (NS_WARN_IF(!chromeWin)) { - return; - } - - nsCOMPtr browserDOMWin; - chromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin)); - if (NS_WARN_IF(!browserDOMWin)) { - return; - } - - // TODO(emilio): This may be simpler by importing a JSM via do_ImportModule - // rather than nsIBrowserDOMWindow. Also, maybe we should make this return a - // promise that unsets the flag when resolved / rejected instead, once the - // print codepaths are unified. - if (NS_FAILED(browserDOMWin->Print(this))) { - return; - } - // The flag will be unset by the printing code. - unsetFlagOnFailure.release(); -} - void BrowsingContext::DidSet(FieldIndex) { MOZ_ASSERT(!GetParent(), "Set muted flag on non top-level context!"); USER_ACTIVATION_LOG("Set audio muted %d for %s browsing context 0x%08" PRIx64, diff --git a/docshell/base/BrowsingContext.h b/docshell/base/BrowsingContext.h index de283e99bae9..7e05949db797 100644 --- a/docshell/base/BrowsingContext.h +++ b/docshell/base/BrowsingContext.h @@ -98,9 +98,6 @@ class WindowProxyHolder; FIELD(CurrentInnerWindowId, uint64_t) \ FIELD(HadOriginalOpener, bool) \ FIELD(IsPopupSpam, bool) \ - /* Whether the window is currently blocked spinning the event loop in the \ - * middle of a window.print() operation */ \ - FIELD(IsAwaitingPrint, bool) \ /* Controls whether the BrowsingContext is currently considered to be \ * activated by a gesture */ \ FIELD(UserActivationState, UserActivation::State) \ @@ -422,8 +419,6 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache { bool FullscreenAllowed() const; - bool IsAwaitingPrint() const { return GetIsAwaitingPrint(); } - float FullZoom() const { return GetFullZoom(); } float TextZoom() const { return GetTextZoom(); } @@ -737,8 +732,6 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache { void DidSet(FieldIndex, bool aOldValue); - void DidSet(FieldIndex, bool aOldValue); - // Ensure that we only set the flag on the top level browsingContext. // And then, we do a pre-order walk in the tree to refresh the // volume of all media elements. diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index ae54ac8578a9..efeb5bee96d0 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -12407,47 +12407,8 @@ void nsDocShell::SetIsPrinting(bool aIsPrinting) { mIsPrintingOrPP = aIsPrinting; } -NS_IMETHODIMP -nsDocShell::InitOrReusePrintPreviewViewer(nsIWebBrowserPrint** aPrintPreview) { - *aPrintPreview = nullptr; -#if NS_PRINT_PREVIEW - nsCOMPtr print = do_QueryInterface(mContentViewer); - if (!print || !print->IsInitializedForPrintPreview()) { - // XXX: Creating a brand new content viewer to host preview every - // time we enter here seems overwork. We could skip ahead to where - // we QI the mContentViewer if the current URI is either about:blank - // or about:printpreview. - Stop(nsIWebNavigation::STOP_ALL); - nsCOMPtr principal = - NullPrincipal::CreateWithInheritedAttributes(this); - nsCOMPtr uri; - NS_NewURI(getter_AddRefs(uri), "about:printpreview"_ns); - // Reuse the null principal for the partitioned principal. - // XXXehsan is that the right principal to use here? - nsresult rv = CreateAboutBlankContentViewer(principal, principal, - /* aCsp = */ nullptr, uri); - NS_ENSURE_SUCCESS(rv, rv); - // Here we manually set current URI since we have just created a - // brand new content viewer (about:blank) to host preview. - SetCurrentURI(uri, nullptr, true, 0); - print = do_QueryInterface(mContentViewer); - NS_ENSURE_STATE(print); - print->InitializeForPrintPreview(); - } - nsCOMPtr result = do_QueryInterface(print); - result.forget(aPrintPreview); - return NS_OK; -#else - return NS_ERROR_NOT_IMPLEMENTED; -#endif -} - NS_IMETHODIMP nsDocShell::ExitPrintPreview() { #if NS_PRINT_PREVIEW -# ifdef DEBUG - nsCOMPtr vp = do_QueryInterface(mContentViewer); - MOZ_ASSERT(vp && vp->IsInitializedForPrintPreview()); -# endif nsCOMPtr viewer = do_QueryInterface(mContentViewer); return viewer->ExitPrintPreview(); #else diff --git a/docshell/base/nsIDocShell.idl b/docshell/base/nsIDocShell.idl index 763b73649e85..3396ac35b4a4 100644 --- a/docshell/base/nsIDocShell.idl +++ b/docshell/base/nsIDocShell.idl @@ -482,24 +482,6 @@ interface nsIDocShell : nsIDocShellTreeItem */ [noscript, notxpcom] void setIsPrinting(in boolean aIsPrinting); - /** - * This method should only be called on a docShell that has been specifically - * created to display a print preview document. If the current document - * viewer isn't initialized for print preview when this method is called, it - * is replaced with a new viewer with an about:blank document (with the URL - * about:printpreview). The viewer is then returned, ready for the print - * preview document to be constructed when viewer.printPreview() is called. - * - * The same viewer will be returned on subsequent calls since various bits of - * code assume that, once created, the viewer is never replaced. Note, - * however, that the viewer's document will be replaced with a new document - * each time printPreview() is called on it (which is what we do to take - * account of print preview settings changes). Replacing the document - * viewer's document breaks the normally unchanging 1:1 relationship between - * a document and its viewer, but that seems to be okay. - */ - nsIWebBrowserPrint initOrReusePrintPreviewViewer(); - /** * Propagated to the print preview document viewer. Must only be called on * a document viewer that has been initialized for print preview. diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index 75facd67eb1c..2d372356575c 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -3210,23 +3210,19 @@ already_AddRefed nsFrameLoader::Print(uint64_t aOuterWindowID, return promise.forget(); } - nsGlobalWindowOuter* outerWindow = + RefPtr outerWindow = nsGlobalWindowOuter::GetOuterWindowWithId(aOuterWindowID); if (NS_WARN_IF(!outerWindow)) { promise->MaybeReject(ErrorResult(NS_ERROR_FAILURE)); return promise.forget(); } - nsCOMPtr webBrowserPrint = - do_GetInterface(ToSupports(outerWindow)); - if (NS_WARN_IF(!webBrowserPrint)) { - promise->MaybeReject(ErrorResult(NS_ERROR_FAILURE)); - return promise.forget(); - } - - nsresult rv = webBrowserPrint->Print(aPrintSettings, listener); - if (NS_FAILED(rv)) { - promise->MaybeReject(ErrorResult(rv)); + ErrorResult rv; + outerWindow->Print(aPrintSettings, listener, + /* aDocShellToCloneInto = */ nullptr, + /* aIsPreview = */ false, rv); + if (rv.Failed()) { + promise->MaybeReject(std::move(rv)); } return promise.forget(); diff --git a/dom/base/nsGlobalWindowInner.cpp b/dom/base/nsGlobalWindowInner.cpp index a5052736a2da..e7ff7d96977b 100644 --- a/dom/base/nsGlobalWindowInner.cpp +++ b/dom/base/nsGlobalWindowInner.cpp @@ -3605,6 +3605,15 @@ void nsGlobalWindowInner::Print(ErrorResult& aError) { FORWARD_TO_OUTER_OR_THROW(PrintOuter, (aError), aError, ); } +Nullable nsGlobalWindowInner::PrintPreview( + nsIPrintSettings* aSettings, nsIWebProgressListener* aListener, + nsIDocShell* aDocShellToCloneInto, ErrorResult& aError) { + FORWARD_TO_OUTER_OR_THROW(Print, + (aSettings, aListener, aDocShellToCloneInto, + /* aIsPreview = */ true, aError), + aError, nullptr); +} + void nsGlobalWindowInner::MoveTo(int32_t aXPos, int32_t aYPos, CallerType aCallerType, ErrorResult& aError) { FORWARD_TO_OUTER_OR_THROW(MoveToOuter, (aXPos, aYPos, aCallerType, aError), diff --git a/dom/base/nsGlobalWindowInner.h b/dom/base/nsGlobalWindowInner.h index 94d97681ac68..aec53952f7bc 100644 --- a/dom/base/nsGlobalWindowInner.h +++ b/dom/base/nsGlobalWindowInner.h @@ -73,6 +73,7 @@ class nsIScriptTimeoutHandler; class nsIBrowserChild; class nsITimeoutHandler; class nsIWebBrowserChrome; +class nsIWebProgressListener; class mozIDOMWindowProxy; class nsScreen; @@ -674,6 +675,9 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget, const mozilla::dom::RequestInit& aInit, mozilla::dom::CallerType aCallerType, mozilla::ErrorResult& aRv); void Print(mozilla::ErrorResult& aError); + mozilla::dom::Nullable PrintPreview( + nsIPrintSettings*, nsIWebProgressListener*, nsIDocShell*, + mozilla::ErrorResult&); void PostMessageMoz(JSContext* aCx, JS::Handle aMessage, const nsAString& aTargetOrigin, const mozilla::dom::Sequence& aTransfer, diff --git a/dom/base/nsGlobalWindowOuter.cpp b/dom/base/nsGlobalWindowOuter.cpp index 5a9a024954e5..eb2ef065be5c 100644 --- a/dom/base/nsGlobalWindowOuter.cpp +++ b/dom/base/nsGlobalWindowOuter.cpp @@ -4,6 +4,8 @@ * 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/. */ +#include "mozilla/Assertions.h" +#include "mozilla/ScopeExit.h" #include "nsGlobalWindow.h" #include @@ -183,6 +185,7 @@ #include "mozilla/dom/Element.h" #include "mozilla/dom/Selection.h" #include "nsFrameLoader.h" +#include "nsFrameLoaderOwner.h" #include "nsXPCOMCID.h" #include "mozilla/Logging.h" #include "prenv.h" @@ -2745,6 +2748,10 @@ void nsGlobalWindowOuter::GetEventTargetParent(EventChainPreVisitor& aVisitor) { } bool nsGlobalWindowOuter::ShouldPromptToBlockDialogs() { + if (!nsContentUtils::GetCurrentJSContext()) { + return false; // non-scripted caller. + } + nsGlobalWindowOuter* topWindowOuter = GetInProcessScriptableTopInternal(); if (!topWindowOuter) { NS_ASSERTION(!mDocShell, @@ -5172,56 +5179,219 @@ void nsGlobalWindowOuter::StopOuter(ErrorResult& aError) { } } +static CallState CollectDocuments(Document& aDoc, + nsTArray>& aDocs) { + aDocs.AppendElement(&aDoc); + auto recurse = [&aDocs](Document& aSubDoc) { + return CollectDocuments(aSubDoc, aDocs); + }; + aDoc.EnumerateSubDocuments(recurse); + return CallState::Continue; +} + +static void DispatchPrintEventToWindowTree( + Document& aDoc, const nsAString& aEvent) { + if (aDoc.IsStaticDocument()) { + return; + } + + nsTArray> targets; + CollectDocuments(aDoc, targets); + for (nsCOMPtr& doc : targets) { + nsContentUtils::DispatchTrustedEvent(doc, doc->GetWindow(), aEvent, + CanBubble::eNo, Cancelable::eNo, + nullptr); + } +} + void nsGlobalWindowOuter::PrintOuter(ErrorResult& aError) { -#ifdef NS_PRINTING if (!AreDialogsEnabled()) { // We probably want to keep throwing here; silently doing nothing is a bit // weird given the typical use cases of print(). - aError.Throw(NS_ERROR_NOT_AVAILABLE); - return; + return aError.ThrowNotSupportedError("Dialogs not enabled for this window"); } if (ShouldPromptToBlockDialogs() && !ConfirmDialogIfNeeded()) { - aError.Throw(NS_ERROR_NOT_AVAILABLE); - return; + return aError.ThrowNotAllowedError("Prompt was canceled by the user"); } - RefPtr kungFuDeathGrip = this; - nsAutoSyncOperation sync(mDoc); + const bool isPreview = StaticPrefs::print_tab_modal_enabled() && + !StaticPrefs::print_always_print_silent(); + Print(nullptr, nullptr, nullptr, isPreview, aError); +} + +Nullable nsGlobalWindowOuter::Print( + nsIPrintSettings* aPrintSettings, nsIWebProgressListener* aListener, + nsIDocShell* aDocShellToCloneInto, bool aIsPreview, ErrorResult& aError) { +#ifdef NS_PRINTING + nsCOMPtr printSettingsService = + do_GetService("@mozilla.org/gfx/printsettings-service;1"); + if (!printSettingsService) { + // we currently return here in headless mode - should we? + aError.ThrowNotSupportedError("No print settings service"); + return nullptr; + } + + RefPtr docToPrint = mDoc; + MOZ_DIAGNOSTIC_ASSERT(docToPrint, + "This gets forwarded from the inner when " + "we have an active window, so there should " + "be a document"); + if (!docToPrint) { + aError.ThrowNotSupportedError("Document is gone"); + return nullptr; + } + + RefPtr sourceBC = docToPrint->GetBrowsingContext(); + MOZ_DIAGNOSTIC_ASSERT(sourceBC); + if (!sourceBC) { + aError.ThrowNotSupportedError("No browsing context"); + return nullptr; + } + + nsAutoSyncOperation sync(docToPrint); EnterModalState(); - auto leave = MakeScopeExit([&] { LeaveModalState(); }); + auto exitModal = MakeScopeExit([&] { LeaveModalState(); }); - if (StaticPrefs::print_tab_modal_enabled()) { - RefPtr bc = GetBrowsingContext(); - if (!bc || bc->IsDiscarded()) { - return; + nsCOMPtr cv; + RefPtr bc; + if (docToPrint->IsStaticDocument() && aIsPreview) { + // We're already a print preview window, just reuse our browsing context / + // content viewer. + // + // TODO(emilio): When the old print preview UI is gone and the new print UI + // auto-closes when printing (bug 1659624), we can remove the aIsPreview + // condition and just reuse the document for printing as well. + bc = sourceBC; + nsCOMPtr docShell = bc->GetDocShell(); + if (!docShell) { + aError.ThrowNotSupportedError("No docshell"); + return nullptr; } - - if (bc->IsAwaitingPrint()) { - // Already printing. - return; + // We could handle this if needed. + if (aDocShellToCloneInto && aDocShellToCloneInto != docShell) { + aError.ThrowNotSupportedError( + "We don't handle cloning a print preview doc into a different " + "docshell"); + return nullptr; } - - MOZ_ALWAYS_SUCCEEDS(bc->SetIsAwaitingPrint(true)); - SpinEventLoopUntil( - [&] { return bc->IsDiscarded() || !bc->IsAwaitingPrint(); }); + docShell->GetContentViewer(getter_AddRefs(cv)); + MOZ_DIAGNOSTIC_ASSERT(cv); } else { - nsCOMPtr printSettingsService = - do_GetService("@mozilla.org/gfx/printsettings-service;1"); - if (!printSettingsService) { - // we currently return here in headless mode - should we? - aError.Throw(NS_ERROR_NOT_AVAILABLE); - return; + if (aDocShellToCloneInto) { + bc = aDocShellToCloneInto->GetBrowsingContext(); + } else { + AutoNoJSAPI nojsapi; + auto printKind = aIsPreview ? PrintKind::PrintPreview : PrintKind::Print; + aError = OpenInternal(EmptyString(), EmptyString(), EmptyString(), + false, // aDialog + false, // aContentModal + true, // aCalledNoScript + false, // aDoJSFixups + true, // aNavigate + nullptr, nullptr, // No args + nullptr, // aLoadState + false, // aForceNoOpener + printKind, getter_AddRefs(bc)); + if (NS_WARN_IF(aError.Failed())) { + return nullptr; + } + } + if (!bc) { + aError.ThrowNotAllowedError("No browsing context"); + return nullptr; + } + nsCOMPtr cloneDocShell = bc->GetDocShell(); + MOZ_DIAGNOSTIC_ASSERT(cloneDocShell); + cloneDocShell->GetContentViewer(getter_AddRefs(cv)); + MOZ_DIAGNOSTIC_ASSERT(cv); + if (!cv) { + aError.ThrowNotSupportedError("Didn't end up with a content viewer"); + return nullptr; } - nsCOMPtr webBrowserPrint = - do_GetInterface(static_cast(this)); - if (!webBrowserPrint) { - aError.Throw(NS_ERROR_NOT_AVAILABLE); - return; + if (bc != sourceBC) { + MOZ_ASSERT(bc->IsTopContent()); + // If we are cloning from a document in a different BrowsingContext, we + // need to make sure to copy over our opener policy information from that + // BrowsingContext. In the case where the source is an iframe, this + // information needs to be copied from the toplevel source + // BrowsingContext, as we may be making a static clone of a single + // subframe. + MOZ_ALWAYS_SUCCEEDS( + bc->SetOpenerPolicy(sourceBC->Top()->GetOpenerPolicy())); + } + + if (RefPtr doc = cv->GetDocument()) { + if (doc->IsShowing()) { + // We're going to drop this document on the floor, in the SetDocument + // call below. Make sure to run OnPageHide() to keep state consistent + // and avoids assertions in the document destructor. + doc->OnPageHide(false, nullptr); + } + } + + // TODO(emilio): Should dispatch this to OOP iframes too. + DispatchPrintEventToWindowTree(*docToPrint, u"beforeprint"_ns); + auto dispatchAfterPrint = MakeScopeExit( + [&] { DispatchPrintEventToWindowTree(*docToPrint, u"afterprint"_ns); }); + + RefPtr clone; + { + nsAutoScriptBlocker blockScripts; + clone = docToPrint->CreateStaticClone(cloneDocShell); + if (!clone) { + aError.ThrowNotSupportedError("Clone operation for printing failed"); + return nullptr; + } + + // Do this now so that we get a script handling object, and thus can + // create our clones. + aError = cv->SetDocument(clone); + if (aError.Failed()) { + return nullptr; + } + + auto pendingFrameClones = clone->TakePendingFrameStaticClones(); + for (const auto& clone : pendingFrameClones) { + RefPtr element = do_QueryObject(clone.mElement); + RefPtr frameLoader = + nsFrameLoader::Create(element, /* aNetworkCreated */ false); + + if (NS_WARN_IF(!frameLoader)) { + continue; + } + + clone.mElement->SetFrameLoader(frameLoader); + + nsCOMPtr docshell; + RefPtr doc; + nsresult rv = frameLoader->FinishStaticClone(clone.mStaticCloneOf, + getter_AddRefs(docshell), + getter_AddRefs(doc)); + if (NS_WARN_IF(NS_FAILED(rv))) { + continue; + } + } } - webBrowserPrint->Print(nullptr, nullptr); } + + nsCOMPtr webBrowserPrint = do_QueryInterface(cv); + if (!webBrowserPrint) { + aError.ThrowNotSupportedError( + "Content viewer didn't implement nsIWebBrowserPrint"); + return nullptr; + } + + if (aIsPreview) { + aError = webBrowserPrint->PrintPreview(aPrintSettings, aListener); + } else { + // Historically we've eaten this error. + webBrowserPrint->Print(aPrintSettings, aListener); + } + return WindowProxyHolder(std::move(bc)); +#else + return nullptr; #endif // NS_PRINTING } @@ -5634,7 +5804,7 @@ nsresult nsGlobalWindowOuter::Open(const nsAString& aUrl, false, // aDoJSFixups true, // aNavigate nullptr, nullptr, // No args - aLoadState, aForceNoOpener, _retval); + aLoadState, aForceNoOpener, PrintKind::None, _retval); } nsresult nsGlobalWindowOuter::OpenJS(const nsAString& aUrl, @@ -5650,7 +5820,7 @@ nsresult nsGlobalWindowOuter::OpenJS(const nsAString& aUrl, nullptr, nullptr, // No args nullptr, // aLoadState false, // aForceNoOpener - _retval); + PrintKind::None, _retval); } // like Open, but attaches to the new window any extra parameters past @@ -5669,7 +5839,7 @@ nsresult nsGlobalWindowOuter::OpenDialog(const nsAString& aUrl, nullptr, aExtraArgument, // Arguments nullptr, // aLoadState false, // aForceNoOpener - _retval); + PrintKind::None, _retval); } // Like Open, but passes aNavigate=false. @@ -5687,7 +5857,7 @@ nsresult nsGlobalWindowOuter::OpenNoNavigate(const nsAString& aUrl, nullptr, nullptr, // No args nullptr, // aLoadState false, // aForceNoOpener - _retval); + PrintKind::None, _retval); } Nullable nsGlobalWindowOuter::OpenDialogOuter( @@ -5712,7 +5882,7 @@ Nullable nsGlobalWindowOuter::OpenDialogOuter( argvArray, nullptr, // Arguments nullptr, // aLoadState false, // aForceNoOpener - getter_AddRefs(dialog)); + PrintKind::None, getter_AddRefs(dialog)); if (!dialog) { return nullptr; } @@ -6900,7 +7070,7 @@ nsresult nsGlobalWindowOuter::OpenInternal( const nsAString& aUrl, const nsAString& aName, const nsAString& aOptions, bool aDialog, bool aContentModal, bool aCalledNoScript, bool aDoJSFixups, bool aNavigate, nsIArray* argv, nsISupports* aExtraArgument, - nsDocShellLoadState* aLoadState, bool aForceNoOpener, + nsDocShellLoadState* aLoadState, bool aForceNoOpener, PrintKind aPrintKind, BrowsingContext** aReturn) { #ifdef DEBUG uint32_t argc = 0; @@ -7051,6 +7221,19 @@ nsresult nsGlobalWindowOuter::OpenInternal( bool isPopupSpamWindow = checkForPopup && (abuseLevel >= PopupBlocker::openControlled); + const auto wwPrintKind = [&] { + switch (aPrintKind) { + case PrintKind::None: + return nsPIWindowWatcher::PRINT_NONE; + case PrintKind::Print: + return nsPIWindowWatcher::PRINT_REGULAR; + case PrintKind::PrintPreview: + return nsPIWindowWatcher::PRINT_PREVIEW; + } + MOZ_ASSERT_UNREACHABLE("Wat"); + return nsPIWindowWatcher::PRINT_NONE; + }(); + { // Reset popup state while opening a window to prevent the // current state from being active the whole time a modal @@ -7063,8 +7246,8 @@ nsresult nsGlobalWindowOuter::OpenInternal( rv = pwwatch->OpenWindow2(this, url, name, options, /* aCalledFromScript = */ true, aDialog, aNavigate, argv, isPopupSpamWindow, - forceNoOpener, forceNoReferrer, aLoadState, - getter_AddRefs(domReturn)); + forceNoOpener, forceNoReferrer, wwPrintKind, + aLoadState, getter_AddRefs(domReturn)); } else { // Force a system caller here so that the window watcher won't screw us // up. We do NOT want this case looking at the JS context on the stack @@ -7083,8 +7266,8 @@ nsresult nsGlobalWindowOuter::OpenInternal( rv = pwwatch->OpenWindow2(this, url, name, options, /* aCalledFromScript = */ false, aDialog, aNavigate, aExtraArgument, isPopupSpamWindow, - forceNoOpener, forceNoReferrer, aLoadState, - getter_AddRefs(domReturn)); + forceNoOpener, forceNoReferrer, wwPrintKind, + aLoadState, getter_AddRefs(domReturn)); } } diff --git a/dom/base/nsGlobalWindowOuter.h b/dom/base/nsGlobalWindowOuter.h index e0e723797c90..53965e551b89 100644 --- a/dom/base/nsGlobalWindowOuter.h +++ b/dom/base/nsGlobalWindowOuter.h @@ -68,6 +68,7 @@ class nsIScriptTimeoutHandler; class nsIBrowserChild; class nsITimeoutHandler; class nsIWebBrowserChrome; +class nsIWebProgressListener; class mozIDOMWindowProxy; class nsDocShellLoadState; @@ -578,7 +579,12 @@ class nsGlobalWindowOuter final : public mozilla::dom::EventTarget, void PromptOuter(const nsAString& aMessage, const nsAString& aInitial, nsAString& aReturn, nsIPrincipal& aSubjectPrincipal, mozilla::ErrorResult& aError); + void PrintOuter(mozilla::ErrorResult& aError); + + mozilla::dom::Nullable Print( + nsIPrintSettings*, nsIWebProgressListener*, nsIDocShell*, bool aIsPreview, + mozilla::ErrorResult&); mozilla::dom::Selection* GetSelectionOuter(); already_AddRefed GetSelection() override; nsScreen* GetScreen(); @@ -737,6 +743,8 @@ class nsGlobalWindowOuter final : public mozilla::dom::EventTarget, private: explicit nsGlobalWindowOuter(uint64_t aWindowID); + enum class PrintKind : uint8_t { None, Print, PrintPreview }; + /** * @param aUrl the URL we intend to load into the window. If aNavigate is * true, we'll actually load this URL into the window. Otherwise, @@ -780,6 +788,9 @@ class nsGlobalWindowOuter final : public mozilla::dom::EventTarget, * aOptions, but without affecting any other window * features. * + * @param aPrintKind Whether this is a browser created for printing, and + * if so for which kind of print. + * * @param aReturn [out] The window that was opened, if any. Will be null if * aForceNoOpener is true of if aOptions contains * "noopener". @@ -792,6 +803,7 @@ class nsGlobalWindowOuter final : public mozilla::dom::EventTarget, bool aDoJSFixups, bool aNavigate, nsIArray* argv, nsISupports* aExtraArgument, nsDocShellLoadState* aLoadState, bool aForceNoOpener, + PrintKind aPrintKind, mozilla::dom::BrowsingContext** aReturn); public: diff --git a/dom/chrome-webidl/BrowsingContext.webidl b/dom/chrome-webidl/BrowsingContext.webidl index b65c44d57625..128d7100a6c6 100644 --- a/dom/chrome-webidl/BrowsingContext.webidl +++ b/dom/chrome-webidl/BrowsingContext.webidl @@ -96,8 +96,6 @@ interface BrowsingContext { [SetterThrows] attribute float textZoom; - [SetterThrows] attribute boolean isAwaitingPrint; - /** * Whether this docshell should save entries in global history. */ diff --git a/dom/interfaces/base/nsIBrowserDOMWindow.idl b/dom/interfaces/base/nsIBrowserDOMWindow.idl index 5bd6b240e575..e93cc8b29595 100644 --- a/dom/interfaces/base/nsIBrowserDOMWindow.idl +++ b/dom/interfaces/base/nsIBrowserDOMWindow.idl @@ -72,6 +72,10 @@ interface nsIBrowserDOMWindow : nsISupports * found, revert to OPEN_NEWTAB behavior. */ const short OPEN_SWITCHTAB = 4; + /** + * Open in a hidden browser. Used for printing. + */ + const short OPEN_PRINT_BROWSER = 4; /** * Values for createContentWindow's and openURI's aFlags parameter. @@ -138,20 +142,6 @@ interface nsIBrowserDOMWindow : nsISupports in short aWhere, in long aFlags, in AString aName); - - /** - * Requests printing of a given browsing context. Note that the browsing - * context may not directly correspond to a tab, it could be from a nested - * frame instead. - * - * @param aSourceBrowsingContext the browsing context that we're interested - * in printing. - * - * @return void, but note that you MUST set `aSourceBrowsingContext.isAwaitingPrint` - * to `false` as a result of this operation. - */ - void print(in BrowsingContext aSourceBrowsingContext); - /** * Load a URI. * @param aURI the URI to open. null is not allowed. To create the window diff --git a/dom/ipc/BrowserChild.cpp b/dom/ipc/BrowserChild.cpp index 89f424e3c135..c34285a483e9 100644 --- a/dom/ipc/BrowserChild.cpp +++ b/dom/ipc/BrowserChild.cpp @@ -836,7 +836,8 @@ BrowserChild::ProvideWindow(nsIOpenWindowInfo* aOpenWindowInfo, RefPtr parent = aOpenWindowInfo->GetParent(); int32_t openLocation = nsWindowWatcher::GetWindowOpenLocation( - parent->GetDOMWindow(), aChromeFlags, aCalledFromJS, aWidthSpecified); + parent->GetDOMWindow(), aChromeFlags, aCalledFromJS, aWidthSpecified, + aOpenWindowInfo->GetIsForPrinting()); // If it turns out we're opening in the current browser, just hand over the // current browser's docshell. @@ -1380,16 +1381,7 @@ void BrowserChild::ZoomToRect(const uint32_t& aPresShellId, mozilla::ipc::IPCResult BrowserChild::RecvActivate() { MOZ_ASSERT(mWebBrowser); - // Ensure that the PresShell exists, otherwise focusing - // is definitely not going to work. GetPresShell should - // create a PresShell if one doesn't exist yet. - RefPtr presShell = GetTopLevelPresShell(); - NS_ASSERTION(presShell, "Need a PresShell to activate!"); - Unused << presShell; - - if (presShell) { - mWebBrowser->FocusActivate(); - } + mWebBrowser->FocusActivate(); return IPC_OK(); } @@ -2256,18 +2248,12 @@ mozilla::ipc::IPCResult BrowserChild::RecvHandleAccessKey( mozilla::ipc::IPCResult BrowserChild::RecvPrint(const uint64_t& aOuterWindowID, const PrintData& aPrintData) { #ifdef NS_PRINTING - nsGlobalWindowOuter* outerWindow = + RefPtr outerWindow = nsGlobalWindowOuter::GetOuterWindowWithId(aOuterWindowID); if (NS_WARN_IF(!outerWindow)) { return IPC_OK(); } - nsCOMPtr webBrowserPrint = - do_GetInterface(ToSupports(outerWindow)); - if (NS_WARN_IF(!webBrowserPrint)) { - return IPC_OK(); - } - nsCOMPtr printSettingsSvc = do_GetService("@mozilla.org/gfx/printsettings-service;1"); if (NS_WARN_IF(!printSettingsSvc)) { @@ -2289,11 +2275,16 @@ mozilla::ipc::IPCResult BrowserChild::RecvPrint(const uint64_t& aOuterWindowID, printSettings->SetPrintSession(printSession); printSettingsSvc->DeserializeToPrintSettings(aPrintData, printSettings); - rv = webBrowserPrint->Print(printSettings, nullptr); - if (NS_WARN_IF(NS_FAILED(rv))) { - return IPC_OK(); + { + IgnoredErrorResult rv; + outerWindow->Print(printSettings, + /* aListener = */ nullptr, + /* aWindowToCloneInto = */ nullptr, + /* aIsPreview = */ false, rv); + if (NS_WARN_IF(rv.Failed())) { + return IPC_OK(); + } } - #endif return IPC_OK(); } diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index e4de43a9c758..2e59130abbeb 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -886,7 +886,8 @@ nsresult ContentChild::ProvideWindowCommon( // load in the current process. bool loadInDifferentProcess = aForceNoOpener && StaticPrefs::dom_noopener_newprocess_enabled() && - !useRemoteSubframes && !sandboxFlagsPropagate; + !useRemoteSubframes && !sandboxFlagsPropagate && + !aOpenWindowInfo->GetIsForPrinting(); if (!loadInDifferentProcess && aURI) { // Only special-case cross-process loads if Fission is disabled. With // Fission enabled, the initial in-process load will automatically be @@ -1123,8 +1124,9 @@ nsresult ContentChild::ProvideWindowCommon( } SendCreateWindow(aTabOpener, parent, newChild, aChromeFlags, aCalledFromJS, - aWidthSpecified, aURI, features, fullZoom, - Principal(triggeringPrincipal), csp, referrerInfo, + aWidthSpecified, aOpenWindowInfo->GetIsForPrinting(), + aOpenWindowInfo->GetIsForPrintPreview(), aURI, features, + fullZoom, Principal(triggeringPrincipal), csp, referrerInfo, aOpenWindowInfo->GetOriginAttributes(), std::move(resolve), std::move(reject)); diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 6e35bbd1fe68..66db1a3cee10 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -15,6 +15,7 @@ #include "chrome/common/process_watcher.h" #include "mozilla/Result.h" +#include "nsIBrowserDOMWindow.h" #ifdef ACCESSIBILITY # include "mozilla/a11y/PDocAccessible.h" @@ -4933,9 +4934,10 @@ bool ContentParent::DeallocPWebBrowserPersistDocumentParent( mozilla::ipc::IPCResult ContentParent::CommonCreateWindow( PBrowserParent* aThisTab, BrowsingContext* aParent, bool aSetOpener, const uint32_t& aChromeFlags, const bool& aCalledFromJS, - const bool& aWidthSpecified, nsIURI* aURIToLoad, const nsCString& aFeatures, - const float& aFullZoom, BrowserParent* aNextRemoteBrowser, - const nsString& aName, nsresult& aResult, + const bool& aWidthSpecified, const bool& aForPrinting, + const bool& aForPrintPreview, nsIURI* aURIToLoad, + const nsCString& aFeatures, const float& aFullZoom, + BrowserParent* aNextRemoteBrowser, const nsString& aName, nsresult& aResult, nsCOMPtr& aNewRemoteTab, bool* aWindowIsNew, int32_t& aOpenLocation, nsIPrincipal* aTriggeringPrincipal, nsIReferrerInfo* aReferrerInfo, bool aLoadURI, @@ -4953,9 +4955,13 @@ mozilla::ipc::IPCResult ContentParent::CommonCreateWindow( openInfo->mForceNoOpener = !aSetOpener; openInfo->mParent = aParent; openInfo->mIsRemote = true; + openInfo->mIsForPrinting = aForPrinting; + openInfo->mIsForPrintPreview = aForPrintPreview; openInfo->mNextRemoteBrowser = aNextRemoteBrowser; openInfo->mOriginAttributes = aOriginAttributes; + MOZ_ASSERT_IF(aForPrintPreview, aForPrinting); + RefPtr topParent = BrowserParent::GetFrom(aThisTab); while (topParent && topParent->GetBrowserBridgeParent()) { topParent = topParent->GetBrowserBridgeParent()->Manager(); @@ -5018,17 +5024,19 @@ mozilla::ipc::IPCResult ContentParent::CommonCreateWindow( } aOpenLocation = nsWindowWatcher::GetWindowOpenLocation( - outerWin, aChromeFlags, aCalledFromJS, aWidthSpecified); + outerWin, aChromeFlags, aCalledFromJS, aWidthSpecified, aForPrinting); MOZ_ASSERT(aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWTAB || - aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWWINDOW); + aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWWINDOW || + aOpenLocation == nsIBrowserDOMWindow::OPEN_PRINT_BROWSER); - if (aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWTAB) { - if (NS_WARN_IF(!browserDOMWin)) { - aResult = NS_ERROR_ABORT; - return IPC_OK(); - } + if (NS_WARN_IF(!browserDOMWin)) { + // Opening in the same window or headless requires an nsIBrowserDOMWindow. + aOpenLocation = nsIBrowserDOMWindow::OPEN_NEWWINDOW; + } + if (aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWTAB || + aOpenLocation == nsIBrowserDOMWindow::OPEN_PRINT_BROWSER) { RefPtr openerElement = do_QueryObject(frame); nsCOMPtr params = @@ -5144,7 +5152,8 @@ mozilla::ipc::IPCResult ContentParent::CommonCreateWindow( mozilla::ipc::IPCResult ContentParent::RecvCreateWindow( PBrowserParent* aThisTab, const MaybeDiscarded& aParent, PBrowserParent* aNewTab, const uint32_t& aChromeFlags, - const bool& aCalledFromJS, const bool& aWidthSpecified, nsIURI* aURIToLoad, + const bool& aCalledFromJS, const bool& aWidthSpecified, + const bool& aForPrinting, const bool& aForPrintPreview, nsIURI* aURIToLoad, const nsCString& aFeatures, const float& aFullZoom, const IPC::Principal& aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp, nsIReferrerInfo* aReferrerInfo, const OriginAttributes& aOriginAttributes, @@ -5223,9 +5232,10 @@ mozilla::ipc::IPCResult ContentParent::RecvCreateWindow( int32_t openLocation = nsIBrowserDOMWindow::OPEN_NEWWINDOW; mozilla::ipc::IPCResult ipcResult = CommonCreateWindow( aThisTab, aParent.get(), !!newBCOpener, aChromeFlags, aCalledFromJS, - aWidthSpecified, aURIToLoad, aFeatures, aFullZoom, newTab, VoidString(), - rv, newRemoteTab, &cwi.windowOpened(), openLocation, aTriggeringPrincipal, - aReferrerInfo, /* aLoadUri = */ false, aCsp, aOriginAttributes); + aWidthSpecified, aForPrinting, aForPrintPreview, aURIToLoad, aFeatures, + aFullZoom, newTab, VoidString(), rv, newRemoteTab, &cwi.windowOpened(), + openLocation, aTriggeringPrincipal, aReferrerInfo, /* aLoadUri = */ false, + aCsp, aOriginAttributes); if (!ipcResult) { return ipcResult; } @@ -5297,7 +5307,8 @@ mozilla::ipc::IPCResult ContentParent::RecvCreateWindowInDifferentProcess( nsresult rv; mozilla::ipc::IPCResult ipcResult = CommonCreateWindow( aThisTab, aParent.get(), /* aSetOpener = */ false, aChromeFlags, - aCalledFromJS, aWidthSpecified, aURIToLoad, aFeatures, aFullZoom, + aCalledFromJS, aWidthSpecified, /* aForPrinting = */ false, + /* aForPrintPreview = */ false, aURIToLoad, aFeatures, aFullZoom, /* aNextRemoteBrowser = */ nullptr, aName, rv, newRemoteTab, &windowIsNew, openLocation, aTriggeringPrincipal, aReferrerInfo, /* aLoadUri = */ true, aCsp, aOriginAttributes); diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index dad28f8b9952..aaf5110a27f9 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -530,7 +530,8 @@ class ContentParent final PBrowserParent* aThisBrowserParent, const MaybeDiscarded& aParent, PBrowserParent* aNewTab, const uint32_t& aChromeFlags, const bool& aCalledFromJS, - const bool& aWidthSpecified, nsIURI* aURIToLoad, + const bool& aWidthSpecified, const bool& aForPrinting, + const bool& aForPrintPreview, nsIURI* aURIToLoad, const nsCString& aFeatures, const float& aFullZoom, const IPC::Principal& aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp, nsIReferrerInfo* aReferrerInfo, @@ -750,7 +751,8 @@ class ContentParent final mozilla::ipc::IPCResult CommonCreateWindow( PBrowserParent* aThisTab, BrowsingContext* aParent, bool aSetOpener, const uint32_t& aChromeFlags, const bool& aCalledFromJS, - const bool& aWidthSpecified, nsIURI* aURIToLoad, + const bool& aWidthSpecified, const bool& aForPrinting, + const bool& aForPrintPreview, nsIURI* aURIToLoad, const nsCString& aFeatures, const float& aFullZoom, BrowserParent* aNextRemoteBrowser, const nsString& aName, nsresult& aResult, nsCOMPtr& aNewRemoteTab, diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 037610827a46..a8f3dbea90fc 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -1441,6 +1441,8 @@ parent: uint32_t aChromeFlags, bool aCalledFromJS, bool aWidthSpecified, + bool aForPrinting, + bool aForPrintPreview, nsIURI aURIToLoad, nsCString aFeatures, float aFullZoom, diff --git a/dom/webidl/Window.webidl b/dom/webidl/Window.webidl index c6d06225fd80..396905a22dc3 100644 --- a/dom/webidl/Window.webidl +++ b/dom/webidl/Window.webidl @@ -22,6 +22,7 @@ interface nsIBrowserDOMWindow; interface XULControllers; interface nsIDOMWindowUtils; +interface nsIPrintSettings; typedef OfflineResourceList ApplicationCache; @@ -256,6 +257,15 @@ typedef OfflineResourceList ApplicationCache; [Throws, Pref="dom.enable_window_print"] void print(); + // Returns a window that you can use for a print preview. + // + // This may reuse an existing window if this window is already a print + // preview document, or if you pass a docshell explicitly. + [Throws, ChromeOnly] + WindowProxy? printPreview(optional nsIPrintSettings? settings = null, + optional nsIWebProgressListener? listener = null, + optional nsIDocShell? docShellToPreviewInto = null); + [Throws, CrossOriginCallable, NeedsSubjectPrincipal, BinaryName="postMessageMoz"] void postMessage(any message, DOMString targetOrigin, optional sequence transfer = []); diff --git a/dom/xul/XULFrameElement.cpp b/dom/xul/XULFrameElement.cpp index 4b8a5e755526..47ae2b85a98d 100644 --- a/dom/xul/XULFrameElement.cpp +++ b/dom/xul/XULFrameElement.cpp @@ -162,8 +162,7 @@ nsresult XULFrameElement::BindToTree(BindContext& aContext, nsINode& aParent) { } void XULFrameElement::UnbindFromTree(bool aNullParent) { - RefPtr frameLoader = GetFrameLoader(); - if (frameLoader) { + if (RefPtr frameLoader = GetFrameLoader()) { frameLoader->Destroy(); } mFrameLoader = nullptr; diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp index eace8889d2a7..651e271a46aa 100644 --- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -11,6 +11,7 @@ #include "mozilla/RestyleManager.h" #include "mozilla/ServoStyleSet.h" #include "mozilla/Telemetry.h" +#include "nsThreadUtils.h" #include "nscore.h" #include "nsCOMPtr.h" #include "nsCRT.h" @@ -3174,14 +3175,11 @@ nsDocumentViewer::Print(nsIPrintSettings* aPrintSettings, NS_IMETHODIMP nsDocumentViewer::PrintPreview(nsIPrintSettings* aPrintSettings, - mozIDOMWindowProxy* aChildDOMWin, nsIWebProgressListener* aWebProgressListener) { # ifdef NS_PRINT_PREVIEW - MOZ_ASSERT(IsInitializedForPrintPreview(), - "For print preview nsIWebBrowserPrint must be from " - "docshell.initOrReusePrintPreviewViewer!"); + RefPtr doc = mDocument.get(); + NS_ENSURE_STATE(doc); - NS_ENSURE_ARG_POINTER(aChildDOMWin); if (GetIsPrinting()) { nsPrintJob::CloseProgressDialog(aWebProgressListener); return NS_ERROR_FAILURE; @@ -3193,11 +3191,6 @@ nsDocumentViewer::PrintPreview(nsIPrintSettings* aPrintSettings, return NS_ERROR_FAILURE; } - nsCOMPtr window = do_QueryInterface(aChildDOMWin); - MOZ_ASSERT(window); - nsCOMPtr doc = window->GetDoc(); - NS_ENSURE_STATE(doc); - NS_ENSURE_STATE(!GetIsPrinting()); // beforeprint event may have caused ContentViewer to be shutdown. NS_ENSURE_STATE(mContainer); @@ -3714,11 +3707,10 @@ void nsDocumentViewer::SetIsPrinting(bool aIsPrinting) { // XXX it always returns false for subdocuments bool nsDocumentViewer::GetIsPrintPreview() const { #ifdef NS_PRINTING - if (mPrintJob) { - return mPrintJob->CreatedForPrintPreview(); - } -#endif + return mPrintJob && mPrintJob->CreatedForPrintPreview(); +#else return false; +#endif } //------------------------------------------------------------ @@ -3787,8 +3779,13 @@ void nsDocumentViewer::OnDonePrinting() { printJob->Destroy(); } - // We are done printing, now cleanup - if (mDeferredWindowClose) { + // We are done printing, now cleanup. For non-print-preview jobs, we are + // actually responsible for cleaning up our whole or window (see + // the OPEN_PRINT_BROWSER code), so gotta run window.close() too. + // + // For print preview jobs the front-end code is responsible for cleaning the + // UI. + if (mDeferredWindowClose || !printJob->CreatedForPrintPreview()) { mDeferredWindowClose = false; if (mContainer) { if (nsCOMPtr win = mContainer->GetWindow()) { @@ -3910,14 +3907,6 @@ void nsDocumentViewer::DestroyPresContext() { mPresContext = nullptr; } -bool nsDocumentViewer::IsInitializedForPrintPreview() { - return mInitializedForPrintPreview; -} - -void nsDocumentViewer::InitializeForPrintPreview() { - mInitializedForPrintPreview = true; -} - void nsDocumentViewer::SetPrintPreviewPresentation(nsViewManager* aViewManager, nsPresContext* aPresContext, PresShell* aPresShell) { diff --git a/layout/base/nsIDocumentViewerPrint.h b/layout/base/nsIDocumentViewerPrint.h index 67518ea3523f..dcf7e4916f24 100644 --- a/layout/base/nsIDocumentViewerPrint.h +++ b/layout/base/nsIDocumentViewerPrint.h @@ -49,16 +49,6 @@ class nsIDocumentViewerPrint : public nsISupports { virtual void OnDonePrinting() = 0; - /** - * Returns true is InitializeForPrintPreview() has been called. - */ - virtual bool IsInitializedForPrintPreview() = 0; - - /** - * Marks this viewer to be used for print preview. - */ - virtual void InitializeForPrintPreview() = 0; - /** * Replaces the current presentation with print preview presentation. */ @@ -79,8 +69,6 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumentViewerPrint, void IncrementDestroyBlockedCount() override; \ void DecrementDestroyBlockedCount() override; \ void OnDonePrinting() override; \ - bool IsInitializedForPrintPreview() override; \ - void InitializeForPrintPreview() override; \ void SetPrintPreviewPresentation(nsViewManager* aViewManager, \ nsPresContext* aPresContext, \ mozilla::PresShell* aPresShell) override; diff --git a/layout/base/tests/chrome/printpreview_bug482976_helper.xhtml b/layout/base/tests/chrome/printpreview_bug482976_helper.xhtml index edffa368d275..9cda50df2b85 100644 --- a/layout/base/tests/chrome/printpreview_bug482976_helper.xhtml +++ b/layout/base/tests/chrome/printpreview_bug482976_helper.xhtml @@ -20,8 +20,8 @@ var ok = window.arguments[0].ok; var todo = window.arguments[0].todo; var SimpleTest = window.arguments[0].SimpleTest; var gWbp; +var gPrintPreviewWin; function printpreview() { - gWbp = frameElts[1].contentWindow.docShell.initOrReusePrintPreviewViewer(); var listener = { onLocationChange: function(webProgress, request, location, flags) { }, onProgressChange: function(webProgress, request, curSelfProgress, @@ -38,14 +38,19 @@ function printpreview() { throw Components.Exception("", Cr.NS_NOINTERFACE); } } + let settings = Cc["@mozilla.org/gfx/printsettings-service;1"] .getService(Ci.nsIPrintSettingsService).globalPrintSettings; - settings.showPrintProgress = false; - gWbp.printPreview(settings, frameElts[0].contentWindow, listener); + settings.showPrintProgress = false; + + gPrintPreviewWin = frameElts[0].contentWindow.printPreview(settings, listener); + gWbp = gPrintPreviewWin.docShell.contentViewer; + gWbp.QueryInterface(Ci.nsIWebBrowserPrint); } function exitprintpreview() { - frameElts[1].contentWindow.docShell.exitPrintPreview(); + gPrintPreviewWin.docShell.exitPrintPreview(); + gPrintPreviewWin.close(); } function finish() { diff --git a/layout/base/tests/chrome/printpreview_helper.xhtml b/layout/base/tests/chrome/printpreview_helper.xhtml index 438edb56544a..add6ec3d8c95 100644 --- a/layout/base/tests/chrome/printpreview_helper.xhtml +++ b/layout/base/tests/chrome/printpreview_helper.xhtml @@ -28,6 +28,7 @@ var todo = window.arguments[0].todo; var info = window.arguments[0].info; var SimpleTest = window.arguments[0].SimpleTest; var gWbp; +var gPrintPreviewWindow; var ctx1; var ctx2; var counter = 0; @@ -38,7 +39,6 @@ var file = Cc["@mozilla.org/file/directory_service;1"] filePath = file.path; function printpreview(options = {}) { - gWbp = frameElts[1].docShell.initOrReusePrintPreviewViewer(); let resolve; let promise = new Promise(r => { resolve = r }); var listener = { @@ -74,6 +74,8 @@ function printpreview(options = {}) { settings.printBGColors = true; settings.headerStrLeft = ""; settings.headerStrRight = ""; + settings.footerStrLeft = ""; + settings.footerStrRight = ""; if (options.settings) { for (let key in options.settings) { settings[key] = options.settings[key]; @@ -85,7 +87,9 @@ function printpreview(options = {}) { function afterprint() { ++after; } frameElts[0].contentWindow.addEventListener("beforeprint", beforeprint, true); frameElts[0].contentWindow.addEventListener("afterprint", afterprint, true); - gWbp.printPreview(settings, frameElts[0].contentWindow, listener); + gPrintPreviewWindow = frameElts[0].contentWindow.printPreview(settings, listener); + gWbp = gPrintPreviewWindow.docShell.contentViewer; + gWbp.QueryInterface(Ci.nsIWebBrowserPrint); is(before, 1, "Should have called beforeprint listener!"); if (!options.hasMozPrintCallback) { // If there's a mozPrintCallback the after print event won't fire until @@ -98,7 +102,8 @@ function printpreview(options = {}) { } function exitprintpreview() { - frameElts[1].contentWindow.docShell.exitPrintPreview(); + gPrintPreviewWindow.docShell.exitPrintPreview(); + gPrintPreviewWindow.close(); } function finish() { @@ -153,7 +158,7 @@ function startTest1() { frameElts[0].contentDocument.body.firstChild.innerHTML = "Print preview"; let ppfinished = printpreview(); - ctx1.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(256,256,256)"); + drawPrintPreviewWindow(ctx1); frameElts[0].contentDocument.body.firstChild.innerHTML = "Galley presentation"; // Add some elements. @@ -170,7 +175,7 @@ function startTest1() { async function finalizeTest1(ppfinished) { await ppfinished; - ctx2.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(256,256,256)"); + drawPrintPreviewWindow(ctx2); exitprintpreview(); ok(compareCanvases(), "Canvas should be the same!"); counter = frameElts[0].contentWindow.counter; @@ -240,13 +245,13 @@ async function compareFormElementPrint(el1, el2, equals) { frameElts[0].contentDocument.body.firstChild.value = frameElts[0].contentDocument.body.firstChild.getAttribute('value'); await printpreview(); - ctx1.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(256,256,256)"); + drawPrintPreviewWindow(ctx1); exitprintpreview(); frameElts[0].contentDocument.body.innerHTML = el2; frameElts[0].contentDocument.body.firstChild.value = frameElts[0].contentDocument.body.firstChild.getAttribute('value'); await printpreview(); - ctx2.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(256,256,256)"); + drawPrintPreviewWindow(ctx2); exitprintpreview(); is(compareCanvases(), equals, "Comparing print preview didn't succeed [" + el1 + " : " + el2 + "]"); @@ -302,14 +307,14 @@ async function runTest7() { frameElts[0].contentDocument.body.firstChild.value = frameElts[0].contentDocument.body.firstChild.getAttribute('value'); await printpreview(); - ctx1.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(255,255,255)"); + drawPrintPreviewWindow(ctx1); exitprintpreview(); frameElts[0].contentDocument.body.innerHTML = "
"; var sr = frameElts[0].contentDocument.body.firstChild.attachShadow({mode: "open"}); sr.innerHTML = contentText; await printpreview(); - ctx2.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(255,255,255)"); + drawPrintPreviewWindow(ctx2); exitprintpreview(); ok(compareCanvases(), "Printing light DOM and shadow DOM should create same output"); @@ -326,7 +331,7 @@ async function runTest8() { iframeElement.setAttribute("src", "printpreview_font_api_ref.html"); }); await printpreview(); - ctx1.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(255,255,255)"); + drawPrintPreviewWindow(ctx1); exitprintpreview(); // Second, snapshot the page with font loaded in JS. @@ -335,7 +340,7 @@ async function runTest8() { iframeElement.setAttribute("src", "printpreview_font_api.html"); }); await printpreview(); - ctx2.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(255,255,255)"); + drawPrintPreviewWindow(ctx2); exitprintpreview(); ok(compareCanvases(), "Printing pages with fonts loaded from CSS and JS should be the same."); @@ -351,7 +356,7 @@ async function runTest9() { `; await printpreview(); - ctx1.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(255,255,255)"); + drawPrintPreviewWindow(ctx1); exitprintpreview(); frameElts[0].contentDocument.body.innerHTML = ` @@ -373,13 +378,19 @@ async function runTest9() { frameElts[0].contentDocument.body.offsetTop; await printpreview(); - ctx2.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(255,255,255)"); + drawPrintPreviewWindow(ctx2); exitprintpreview(); ok(compareCanvases(), "Printing subtrees should create same output"); requestAnimationFrame(function() { setTimeout(runTest10); } ); } +function drawPrintPreviewWindow(ctx) { + ctx.canvas.width = gPrintPreviewWindow.innerWidth; + ctx.canvas.height = gPrintPreviewWindow.innerHeight; + ctx.drawWindow(gPrintPreviewWindow, 0, 0, gPrintPreviewWindow.innerWidth, gPrintPreviewWindow.innerHeight, "rgb(255, 255, 255)"); +} + // Test for bug 1524640 async function runTest10() { // Test that fonts loaded during mozprint callback are loaded into the cloned @@ -396,7 +407,7 @@ async function runTest10() { }); await printpreview({ hasMozPrintCallback: true }); await mozPrintCallbackDone; - ctx1.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(255,255,255)"); + drawPrintPreviewWindow(ctx1); exitprintpreview(); // Second, snapshot the page with font loaded in JS. @@ -410,7 +421,7 @@ async function runTest10() { await printpreview({ hasMozPrintCallback: true }); // Wait for the mozprintcallback to finish. await mozPrintCallbackDone; - ctx2.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(255,255,255)"); + drawPrintPreviewWindow(ctx2); exitprintpreview(); ok(compareCanvases(), "Printing pages with fonts loaded from a mozPrintCallback should be the same."); @@ -442,7 +453,7 @@ async function compareFiles(src1, src2, options = {}) { } await printpreview(options.test || options); - ctx1.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(255,255,255)"); + drawPrintPreviewWindow(ctx1); exitprintpreview(); await new Promise((resolve) => { @@ -451,7 +462,7 @@ async function compareFiles(src1, src2, options = {}) { }); await printpreview(options.ref || options); - ctx2.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(255,255,255)"); + drawPrintPreviewWindow(ctx2); exitprintpreview(); ok(compareCanvases(options), `Printing ${src1} and ${src2} should produce the same results`); diff --git a/layout/printing/nsPrintJob.cpp b/layout/printing/nsPrintJob.cpp index 885ee4512d68..cdd0a32a4143 100644 --- a/layout/printing/nsPrintJob.cpp +++ b/layout/printing/nsPrintJob.cpp @@ -173,27 +173,6 @@ static void DumpPrintObjectsTreeLayout(const UniquePtr& aPO, # define DUMP_DOC_TREELAYOUT #endif -static CallState CollectDocuments(Document& aDoc, - nsTArray>& aDocs) { - aDocs.AppendElement(&aDoc); - auto recurse = [&aDocs](Document& aSubDoc) { - return CollectDocuments(aSubDoc, aDocs); - }; - aDoc.EnumerateSubDocuments(recurse); - return CallState::Continue; -} - -MOZ_CAN_RUN_SCRIPT -static void DispatchEventToWindowTree(Document& aDoc, const nsAString& aEvent) { - nsTArray> targets; - CollectDocuments(aDoc, targets); - for (nsCOMPtr& doc : targets) { - nsContentUtils::DispatchTrustedEvent(doc, doc->GetWindow(), aEvent, - CanBubble::eNo, Cancelable::eNo, - nullptr); - } -} - // ------------------------------------------------------- // Helpers // ------------------------------------------------------- @@ -296,39 +275,12 @@ static void BuildNestedPrintObjects(const UniquePtr& aParentPO, aPrintData->mSelectionRoot = aPrintData->mPrintObject.get(); } - nsTArray pendingClones = - aParentPO->mDocument->TakePendingFrameStaticClones(); - for (auto& clone : pendingClones) { - if (NS_WARN_IF(!clone.mStaticCloneOf)) { - continue; - } - - RefPtr element = do_QueryObject(clone.mElement); - RefPtr frameLoader = - nsFrameLoader::Create(element, /* aNetworkCreated */ false); - clone.mElement->SetFrameLoader(frameLoader); - - nsCOMPtr docshell; - RefPtr doc; - nsresult rv = frameLoader->FinishStaticClone( - clone.mStaticCloneOf, getter_AddRefs(docshell), getter_AddRefs(doc)); - if (NS_WARN_IF(NS_FAILED(rv))) { - continue; - } - - nsIDocShell* sourceDocShell = - clone.mStaticCloneOf->GetDocShell(IgnoreErrors()); - if (!sourceDocShell) { - continue; - } - - Document* sourceDoc = sourceDocShell->GetDocument(); - if (!sourceDoc) { - continue; - } + for (auto& bc : aParentPO->mDocShell->GetBrowsingContext()->Children()) { + nsCOMPtr docShell = bc->GetDocShell(); + RefPtr doc = docShell->GetDocument(); auto childPO = MakeUnique(); - rv = childPO->InitAsNestedObject(docshell, doc, aParentPO.get()); + nsresult rv = childPO->InitAsNestedObject(docShell, doc, aParentPO.get()); if (NS_FAILED(rv)) { MOZ_ASSERT_UNREACHABLE("Init failed?"); } @@ -483,12 +435,6 @@ nsresult nsPrintJob::Initialize(nsIDocumentViewerPrint* aDocViewerPrint, mDocShell = do_GetWeakReference(aDocShell); mScreenDPI = aScreenDPI; - // XXX We should not be storing this. The original document that the user - // selected to print can be mutated while print preview is open. Anything - // we need to know about the original document should be checked and stored - // here instead. - mOriginalDoc = aOriginalDoc; - // Anything state that we need from aOriginalDoc must be fetched and stored // here, since the document that the user selected to print may mutate // across consecutive PrintPreview() calls. @@ -606,7 +552,9 @@ nsresult nsPrintJob::CommonPrint(bool aIsPrintPreview, nsresult nsPrintJob::DoCommonPrint(bool aIsPrintPreview, nsIPrintSettings* aPrintSettings, nsIWebProgressListener* aWebProgressListener, - Document* aSourceDoc) { + Document* aDoc) { + MOZ_ASSERT(aDoc->IsStaticDocument()); + nsresult rv; // Grab the new instance with local variable to guarantee that it won't be @@ -644,27 +592,16 @@ nsresult nsPrintJob::DoCommonPrint(bool aIsPrintPreview, } // Get the document from the currently focused window. - RefPtr focusedDoc = FindFocusedDocument(); + RefPtr focusedDoc = FindFocusedDocument(aDoc); // Get the docshell for this documentviewer nsCOMPtr docShell(do_QueryReferent(mDocShell, &rv)); NS_ENSURE_SUCCESS(rv, rv); - if (!aSourceDoc->IsStaticDocument()) { - // This is the original document. We must send 'beforeprint' and - // 'afterprint' events to give the document the chance to make changes - // for print output. (Obviously if `aSourceDoc` is a clone, it already - // has these changes.) - DispatchEventToWindowTree(*aSourceDoc, u"beforeprint"_ns); - if (mIsDestroying) { - return NS_ERROR_FAILURE; - } - } - { nsAutoScriptBlocker scriptBlocker; printData->mPrintObject = MakeUnique(); - rv = printData->mPrintObject->InitAsRootObject(docShell, aSourceDoc, + rv = printData->mPrintObject->InitAsRootObject(docShell, aDoc, mIsCreatingPrintPreview); NS_ENSURE_SUCCESS(rv, rv); @@ -677,10 +614,6 @@ nsresult nsPrintJob::DoCommonPrint(bool aIsPrintPreview, BuildNestedPrintObjects(printData->mPrintObject, focusedDoc, printData); } - if (!aSourceDoc->IsStaticDocument()) { - DispatchEventToWindowTree(*aSourceDoc, u"afterprint"_ns); - } - // The nsAutoScriptBlocker above will now have been destroyed, which may // cause our print/print-preview operation to finish. In this case, we // should immediately return an error code so that the root caller knows @@ -769,7 +702,7 @@ nsresult nsPrintJob::DoCommonPrint(bool aIsPrintPreview, nsPIDOMWindowOuter* domWin = nullptr; // We leave domWin as nullptr to indicate a call for print preview. if (!mIsCreatingPrintPreview) { - domWin = mOriginalDoc->GetWindow(); + domWin = aDoc->GetOriginalDocument()->GetWindow(); NS_ENSURE_TRUE(domWin, NS_ERROR_FAILURE); if (printSilently) { @@ -883,7 +816,7 @@ nsresult nsPrintJob::DoCommonPrint(bool aIsPrintPreview, if (mIsCreatingPrintPreview) { bool notifyOnInit = false; - ShowPrintProgress(false, notifyOnInit); + ShowPrintProgress(false, notifyOnInit, aDoc); if (!notifyOnInit) { SuppressPrintPreviewUserEvents(); @@ -893,7 +826,7 @@ nsresult nsPrintJob::DoCommonPrint(bool aIsPrintPreview, } } else { bool doNotify; - ShowPrintProgress(true, doNotify); + ShowPrintProgress(true, doNotify, aDoc); if (!doNotify) { // Print listener setup... printData->OnStartPrinting(); @@ -949,19 +882,6 @@ nsresult nsPrintJob::Print(Document* aSourceDoc, nsresult nsPrintJob::PrintPreview( Document* aSourceDoc, nsIPrintSettings* aPrintSettings, nsIWebProgressListener* aWebProgressListener) { - // Get the DocShell and see if it is busy - // (We can't Print Preview this document if it is still busy) - nsCOMPtr docShell(do_QueryReferent(mDocShell)); - NS_ENSURE_STATE(docShell); - - auto busyFlags = docShell->GetBusyFlags(); - if (busyFlags != nsIDocShell::BUSY_FLAGS_NONE) { - CloseProgressDialog(aWebProgressListener); - FirePrintingErrorEvent(NS_ERROR_GFX_PRINTER_DOC_IS_BUSY); - return NS_ERROR_FAILURE; - } - - // Document is not busy -- go ahead with the Print Preview return CommonPrint(true, aPrintSettings, aWebProgressListener, aSourceDoc); } @@ -1007,7 +927,8 @@ already_AddRefed nsPrintJob::GetCurrentPrintSettings() { //---------------------------------------------------------------------- // Set up to use the "pluggable" Print Progress Dialog -void nsPrintJob::ShowPrintProgress(bool aIsForPrinting, bool& aDoNotify) { +void nsPrintJob::ShowPrintProgress(bool aIsForPrinting, bool& aDoNotify, + Document* aDoc) { // default to not notifying, that if something here goes wrong // or we aren't going to show the progress dialog we can straight into // reflowing the doc for printing. @@ -1046,7 +967,7 @@ void nsPrintJob::ShowPrintProgress(bool aIsForPrinting, bool& aDoNotify) { return; } - nsPIDOMWindowOuter* domWin = mOriginalDoc->GetWindow(); + nsPIDOMWindowOuter* domWin = aDoc->GetOriginalDocument()->GetWindow(); if (!domWin) return; nsCOMPtr printProgressListener; @@ -2424,11 +2345,11 @@ void nsPrintJob::SetIsPrintPreview(bool aIsPrintPreview) { } } -Document* nsPrintJob::FindFocusedDocument() const { +Document* nsPrintJob::FindFocusedDocument(Document* aDoc) const { nsIFocusManager* fm = nsFocusManager::GetFocusManager(); NS_ENSURE_TRUE(fm, nullptr); - nsPIDOMWindowOuter* window = mOriginalDoc->GetWindow(); + nsPIDOMWindowOuter* window = aDoc->GetOriginalDocument()->GetWindow(); NS_ENSURE_TRUE(window, nullptr); nsCOMPtr rootWindow = window->GetPrivateRoot(); diff --git a/layout/printing/nsPrintJob.h b/layout/printing/nsPrintJob.h index 2d939183144f..561281915221 100644 --- a/layout/printing/nsPrintJob.h +++ b/layout/printing/nsPrintJob.h @@ -178,7 +178,7 @@ class nsPrintJob final : public nsIObserver, nsresult ReflowPrintObject(const mozilla::UniquePtr& aPO); void CalcNumPrintablePages(int32_t& aNumPages); - void ShowPrintProgress(bool aIsForPrinting, bool& aDoNotify); + void ShowPrintProgress(bool aIsForPrinting, bool& aDoNotify, Document* aDoc); void SetURLAndTitleOnProgressParams( const mozilla::UniquePtr& aPO, nsIPrintProgressParams* aParams); @@ -190,8 +190,11 @@ class nsPrintJob final : public nsIObserver, /** * @return The document from the focused windows for a document viewer. + * + * FIXME: This is somewhat unsound, this looks at the original document, which + * could've mutated after print was initiated. */ - Document* FindFocusedDocument() const; + Document* FindFocusedDocument(Document* aDoc) const; /// Customizes the behaviour of GetDisplayTitleAndURL. enum class DocTitleDefault : uint32_t { eDocURLElseFallback, eFallback }; diff --git a/layout/printing/nsPrintObject.cpp b/layout/printing/nsPrintObject.cpp index c3182926cf3a..ebbde6012059 100644 --- a/layout/printing/nsPrintObject.cpp +++ b/layout/printing/nsPrintObject.cpp @@ -62,59 +62,13 @@ nsresult nsPrintObject::InitAsRootObject(nsIDocShell* aDocShell, Document* aDoc, NS_ENSURE_STATE(aDocShell); NS_ENSURE_STATE(aDoc); - if (aForPrintPreview) { - nsCOMPtr viewer; - aDocShell->GetContentViewer(getter_AddRefs(viewer)); - if (viewer && viewer->GetDocument() && viewer->GetDocument()->IsShowing()) { - // We're about to discard this document, and it needs mIsShowing to be - // false to avoid triggering the assertion in its dtor. - viewer->GetDocument()->OnPageHide(false, nullptr); - } - mDocShell = aDocShell; - } else { - // When doing an actual print, we create a BrowsingContext/nsDocShell that - // is detached from any browser window or tab. - - // Create a new BrowsingContext to create our DocShell in. - RefPtr bc = BrowsingContext::CreateIndependent( - nsDocShell::Cast(aDocShell)->GetBrowsingContext()->GetType()); - - // Create a container docshell for printing. - mDocShell = nsDocShell::Create(bc); - NS_ENSURE_TRUE(mDocShell, NS_ERROR_OUT_OF_MEMORY); - - mDidCreateDocShell = true; - MOZ_ASSERT(mDocShell->ItemType() == aDocShell->ItemType()); - - mTreeOwner = do_GetInterface(aDocShell); - mDocShell->SetTreeOwner(mTreeOwner); - - // Make sure nsDocShell::EnsureContentViewer() is called: - mozilla::Unused << nsDocShell::Cast(mDocShell)->GetDocument(); - } + MOZ_ASSERT(aDoc->IsStaticDocument()); - // If we are cloning from a document in a different BrowsingContext, we need - // to make sure to copy over our opener policy information from that - // BrowsingContext. - BrowsingContext* targetBC = mDocShell->GetBrowsingContext(); - BrowsingContext* sourceBC = aDoc->GetBrowsingContext(); - NS_ENSURE_STATE(sourceBC); - if (targetBC != sourceBC) { - MOZ_ASSERT(targetBC->IsTopContent()); - // In the case where the source is an iframe, this information needs to be - // copied from the toplevel source BrowsingContext, as we may be making a - // static clone of a single subframe. - MOZ_ALWAYS_SUCCEEDS( - targetBC->SetOpenerPolicy(sourceBC->Top()->GetOpenerPolicy())); - } - - mDocument = aDoc->CreateStaticClone(mDocShell); - NS_ENSURE_STATE(mDocument); + mDocShell = aDocShell; + mDocument = aDoc; - nsCOMPtr viewer; - mDocShell->GetContentViewer(getter_AddRefs(viewer)); - NS_ENSURE_STATE(viewer); - viewer->SetDocument(mDocument); + // Ensure the document has no presentation. + DestroyPresentation(); return NS_OK; } @@ -141,20 +95,22 @@ nsresult nsPrintObject::InitAsNestedObject(nsIDocShell* aDocShell, // Assume something iframe-like, i.e. iframe, object, or embed mFrameType = eIFrame; } - return NS_OK; } //------------------------------------------------------------------ // Resets PO by destroying the presentation void nsPrintObject::DestroyPresentation() { - if (mPresShell) { - mPresShell->EndObservingDocument(); - nsAutoScriptBlocker scriptBlocker; - RefPtr presShell = mPresShell; - mPresShell = nullptr; - presShell->Destroy(); + if (mDocument) { + if (RefPtr ps = mDocument->GetPresShell()) { + MOZ_DIAGNOSTIC_ASSERT(!mPresShell || ps == mPresShell); + mPresShell = nullptr; + nsAutoScriptBlocker scriptBlocker; + ps->EndObservingDocument(); + ps->Destroy(); + } } + mPresShell = nullptr; mPresContext = nullptr; mViewManager = nullptr; } diff --git a/mobile/android/modules/geckoview/GeckoViewNavigation.jsm b/mobile/android/modules/geckoview/GeckoViewNavigation.jsm index a7440dc8888b..b7ae9fea986c 100644 --- a/mobile/android/modules/geckoview/GeckoViewNavigation.jsm +++ b/mobile/android/modules/geckoview/GeckoViewNavigation.jsm @@ -497,14 +497,6 @@ class GeckoViewNavigation extends GeckoViewModule { return browser && browser.browsingContext; } - // nsIBrowserDOMWindow. - print(aBrowsingContext) { - aBrowsingContext.isAwaitingPrint = false; - throw new Error( - "window.print() is not exposed on android so this should not be reachable" - ); - } - // nsIBrowserDOMWindow. openURIInFrame(aUri, aParams, aWhere, aFlags, aNextRemoteTabId, aName) { const browser = this.handleOpenUri( diff --git a/toolkit/actors/PrintingChild.jsm b/toolkit/actors/PrintingChild.jsm index a9af063fede1..c5bc104bfd00 100644 --- a/toolkit/actors/PrintingChild.jsm +++ b/toolkit/actors/PrintingChild.jsm @@ -360,16 +360,14 @@ class PrintingChild extends ActorChild { this.mm.sendAsyncMessage("Printing:Preview:Entered", { failed: true, }); - browsingContext.isAwaitingPrint = false; return; } + try { let listener = new PrintingListener(this.mm); this.printPreviewInitializingInfo = { changingBrowsers }; - docShell - .initOrReusePrintPreviewViewer() - .printPreview(printSettings, contentWindow, listener); + contentWindow.printPreview(printSettings, listener, docShell); } catch (error) { // This might fail if we, for example, attempt to print a XUL document. // In that case, we inform the parent to bail out of print preview. @@ -379,8 +377,6 @@ class PrintingChild extends ActorChild { failed: true, }); } - - browsingContext.isAwaitingPrint = false; }; // If printPreviewInitializingInfo.entered is not set we are still in the @@ -400,27 +396,26 @@ class PrintingChild extends ActorChild { // In that case, we inform the parent to bail out of print preview. Cu.reportError(error); this.mm.sendAsyncMessage("Printing:Preview:Entered", { failed: true }); - browsingContext.isAwaitingPrint = false; } } exitPrintPreview(glo) { this.printPreviewInitializingInfo = null; - this.docShell.initOrReusePrintPreviewViewer().exitPrintPreview(); + this.docShell.exitPrintPreview(); } updatePageCount() { - let numPages = this.docShell.initOrReusePrintPreviewViewer() - .printPreviewNumPages; + let cv = this.docShell.contentViewer; + cv.QueryInterface(Ci.nsIWebBrowserPrint); this.mm.sendAsyncMessage("Printing:Preview:UpdatePageCount", { - numPages, + numPages: cv.printPreviewNumPages, }); } navigate(navType, pageNum) { - this.docShell - .initOrReusePrintPreviewViewer() - .printPreviewScrollToPage(navType, pageNum); + let cv = this.docShell.contentViewer; + cv.QueryInterface(Ci.nsIWebBrowserPrint); + cv.printPreviewScrollToPage(navType, pageNum); } } diff --git a/toolkit/components/browser/nsIWebBrowserPrint.idl b/toolkit/components/browser/nsIWebBrowserPrint.idl index 70131cf71fbe..bca31a177907 100644 --- a/toolkit/components/browser/nsIWebBrowserPrint.idl +++ b/toolkit/components/browser/nsIWebBrowserPrint.idl @@ -94,13 +94,11 @@ interface nsIWebBrowserPrint : nsISupports * * @param aThePrintSettings - Printer Settings for the print preview, if aThePrintSettings is null * then the global PS will be used. - * @param aChildDOMWin - DOM Window to be print previewed. * @param aWPListener - is updated during the printpreview * @return void */ - void printPreview(in nsIPrintSettings aThePrintSettings, - in mozIDOMWindowProxy aChildDOMWin, - in nsIWebProgressListener aWPListener); + [noscript] void printPreview(in nsIPrintSettings aThePrintSettings, + in nsIWebProgressListener aWPListener); /** * @param aNavType - navigation enum diff --git a/toolkit/components/printing/content/print.js b/toolkit/components/printing/content/print.js index 157fa6c199ad..7993493c8b19 100644 --- a/toolkit/components/printing/content/print.js +++ b/toolkit/components/printing/content/print.js @@ -83,11 +83,27 @@ var PrintEventHandler = { // is initiated and the print preview clone must be a snapshot from the // time that the print was started. let sourceBrowsingContext = this.getSourceBrowsingContext(); + this.previewBrowser = this._createPreviewBrowser(sourceBrowsingContext); + + // Get the temporary browser that will previously have been created for the + // platform code to generate the static clone printing doc into if this + // print is for a window.print() call. In that case we steal the browser's + // docshell to get the static clone, then discard it. + let existingBrowser = window.arguments[0].getProperty("previewBrowser"); + if (existingBrowser) { + sourceBrowsingContext = existingBrowser.browsingContext; + this.previewBrowser.swapDocShells(existingBrowser); + existingBrowser.remove(); + } else { + this.previewBrowser.loadURI("about:printpreview", { + triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), + }); + } + this.originalSourceContentTitle = sourceBrowsingContext.currentWindowContext.documentTitle; this.originalSourceCurrentURI = sourceBrowsingContext.currentWindowContext.documentURI.spec; - this.previewBrowser = this._createPreviewBrowser(sourceBrowsingContext); // First check the available destinations to ensure we get settings for an // accessible printer. @@ -168,10 +184,6 @@ var PrintEventHandler = { previewStack.append(printPreviewBrowser); ourBrowser.parentElement.prepend(previewStack); - printPreviewBrowser.loadURI("about:printpreview", { - triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), - }); - return printPreviewBrowser; }, diff --git a/toolkit/components/printing/content/printUtils.js b/toolkit/components/printing/content/printUtils.js index cd3c3bfa34a4..94ad277126c1 100644 --- a/toolkit/components/printing/content/printUtils.js +++ b/toolkit/components/printing/content/printUtils.js @@ -72,6 +72,12 @@ XPCOMUtils.defineLazyPreferenceGetter( false ); +ChromeUtils.defineModuleGetter( + this, + "PromptUtils", + "resource://gre/modules/SharedPromptUtils.jsm" +); + var gFocusedElement = null; var PrintUtils = { @@ -138,22 +144,33 @@ var PrintUtils = { * * @param aBrowsingContext * The BrowsingContext of the window to print. + * @param aExistingPreviewBrowser + * An existing browser created for printing from window.print(). */ - async _openTabModalPrint(aBrowsingContext) { - let sourceBrowser = aBrowsingContext.embedderElement; + _openTabModalPrint(aBrowsingContext, aExistingPreviewBrowser) { + let sourceBrowser = aBrowsingContext.top.embedderElement; let previewBrowser = this.getPreviewBrowser(sourceBrowser); - if (previewBrowser) { // Don't open another dialog if we're already printing. - aBrowsingContext.isAwaitingPrint = false; + // + // XXX This can be racy can't it? getPreviewBrowser looks at browser that + // we set up after opening the dialog. But I guess worst case we just + // open two dialogs so... + if (aExistingPreviewBrowser) { + aExistingPreviewBrowser.remove(); + } return; } + // Create a preview browser. + let args = PromptUtils.objectToPropBag({ + previewBrowser: aExistingPreviewBrowser, + }); let dialogBox = gBrowser.getTabDialogBox(sourceBrowser); dialogBox.open( `chrome://global/content/print.html?browsingContextId=${aBrowsingContext.id}`, "resizable=no", - null, + args, { sizeTo: "available" } ); }, @@ -166,13 +183,51 @@ var PrintUtils = { * The BrowsingContext of the window to print. * Note that the browsing context could belong to a subframe of the * tab that called window.print, or similar shenanigans. + * @param aOpenWindowInfo + * Non-null if this call comes from window.print(). This is the + * nsIOpenWindowInfo object that has to be passed down to + * createBrowser in order for the child process to clone into it. */ - startPrintWindow(aBrowsingContext) { - if (PRINT_TAB_MODAL && !PRINT_ALWAYS_SILENT) { - this._openTabModalPrint(aBrowsingContext); - } else { - this.printWindow(aBrowsingContext); + startPrintWindow(aBrowsingContext, aOpenWindowInfo) { + let browser = null; + if (aOpenWindowInfo) { + browser = gBrowser.createBrowser({ + remoteType: aBrowsingContext.currentRemoteType, + openWindowInfo: aOpenWindowInfo, + skipLoad: false, + }); + // When the print process finishes, we get closed by + // nsDocumentViewer::OnDonePrinting, or by the print preview code. + // + // When that happens, we should remove us from the DOM if connected. + browser.addEventListener("DOMWindowClose", function(e) { + if (browser.isConnected) { + browser.remove(); + } + e.stopPropagation(); + e.preventDefault(); + }); + browser.style.visibility = "collapse"; + document.documentElement.appendChild(browser); } + + if ( + PRINT_TAB_MODAL && + !PRINT_ALWAYS_SILENT && + (!aOpenWindowInfo || aOpenWindowInfo.isForPrintPreview) + ) { + this._openTabModalPrint(aBrowsingContext, browser); + return browser; + } + + if (browser) { + // Legacy print dialog or silent printing, the content process will print + // in this . + return browser; + } + + this.printWindow(aBrowsingContext, null); + return null; }, /** diff --git a/toolkit/components/windowcreator/nsIWindowProvider.idl b/toolkit/components/windowcreator/nsIWindowProvider.idl index 307a8acf7fa0..135b0b0bd9ae 100644 --- a/toolkit/components/windowcreator/nsIWindowProvider.idl +++ b/toolkit/components/windowcreator/nsIWindowProvider.idl @@ -93,6 +93,7 @@ interface nsIWindowProvider : nsISupports * @see nsIWindowWatcher for more information on aFeatures. * @see nsIWebBrowserChrome for more information on aChromeFlags. */ + [noscript] BrowsingContext provideWindow(in nsIOpenWindowInfo aOpenWindowInfo, in unsigned long aChromeFlags, in boolean aCalledFromJS, diff --git a/toolkit/components/windowwatcher/nsIOpenWindowInfo.idl b/toolkit/components/windowwatcher/nsIOpenWindowInfo.idl index 54894b9382bb..edc141b70c82 100644 --- a/toolkit/components/windowwatcher/nsIOpenWindowInfo.idl +++ b/toolkit/components/windowwatcher/nsIOpenWindowInfo.idl @@ -49,6 +49,17 @@ interface nsIOpenWindowInfo : nsISupports { [infallible] readonly attribute boolean forceNoOpener; + /** Whether this is a window opened for printing */ + [infallible] + readonly attribute boolean isForPrinting; + + /** + * Whether this is a window opened for print preview. + * When this is true, isForPrinting is necessarily true as well. + */ + [infallible] + readonly attribute boolean isForPrintPreview; + /** BrowserParent instance to use in the new window */ [notxpcom, nostdcall] BrowserParent getNextRemoteBrowser(); diff --git a/toolkit/components/windowwatcher/nsOpenWindowInfo.cpp b/toolkit/components/windowwatcher/nsOpenWindowInfo.cpp index 4eeae1b3c8e6..16de91041af1 100644 --- a/toolkit/components/windowwatcher/nsOpenWindowInfo.cpp +++ b/toolkit/components/windowwatcher/nsOpenWindowInfo.cpp @@ -22,6 +22,16 @@ NS_IMETHODIMP nsOpenWindowInfo::GetIsRemote(bool* aIsRemote) { return NS_OK; } +NS_IMETHODIMP nsOpenWindowInfo::GetIsForPrintPreview(bool* aIsForPrinPreview) { + *aIsForPrinPreview = mIsForPrintPreview; + return NS_OK; +} + +NS_IMETHODIMP nsOpenWindowInfo::GetIsForPrinting(bool* aIsForPrinting) { + *aIsForPrinting = mIsForPrinting; + return NS_OK; +} + NS_IMETHODIMP nsOpenWindowInfo::GetForceNoOpener(bool* aForceNoOpener) { *aForceNoOpener = mForceNoOpener; return NS_OK; diff --git a/toolkit/components/windowwatcher/nsOpenWindowInfo.h b/toolkit/components/windowwatcher/nsOpenWindowInfo.h index 24a77da01de9..a7983b1bdfad 100644 --- a/toolkit/components/windowwatcher/nsOpenWindowInfo.h +++ b/toolkit/components/windowwatcher/nsOpenWindowInfo.h @@ -20,6 +20,8 @@ class nsOpenWindowInfo : public nsIOpenWindowInfo { bool mForceNoOpener = false; bool mIsRemote = false; + bool mIsForPrinting = false; + bool mIsForPrintPreview = false; RefPtr mNextRemoteBrowser; mozilla::OriginAttributes mOriginAttributes; RefPtr mParent; diff --git a/toolkit/components/windowwatcher/nsPIWindowWatcher.idl b/toolkit/components/windowwatcher/nsPIWindowWatcher.idl index 690499f640f4..3c4cfd620187 100644 --- a/toolkit/components/windowwatcher/nsPIWindowWatcher.idl +++ b/toolkit/components/windowwatcher/nsPIWindowWatcher.idl @@ -43,6 +43,12 @@ interface nsPIWindowWatcher : nsISupports */ void removeWindow(in mozIDOMWindowProxy aWindow); + cenum PrintKind : 8 { + PRINT_NONE, + PRINT_REGULAR, + PRINT_PREVIEW, + }; + /** Like the public interface's open(), but can handle openDialog-style arguments and calls which shouldn't result in us navigating the window. @@ -82,6 +88,7 @@ interface nsPIWindowWatcher : nsISupports (which is determined based on the JS stack and the value of aParent). This is not guaranteed, however. */ + [noscript] BrowsingContext openWindow2(in mozIDOMWindowProxy aParent, in ACString aUrl, in ACString aName, in ACString aFeatures, in boolean aCalledFromScript, @@ -91,6 +98,7 @@ interface nsPIWindowWatcher : nsISupports in boolean aIsPopupSpam, in boolean aForceNoOpener, in boolean aForceNoReferrer, + in nsPIWindowWatcher_PrintKind aPrintKind, in nsDocShellLoadStatePtr aLoadState); /** diff --git a/toolkit/components/windowwatcher/nsWindowWatcher.cpp b/toolkit/components/windowwatcher/nsWindowWatcher.cpp index b1292f9b3050..2fec933fd165 100644 --- a/toolkit/components/windowwatcher/nsWindowWatcher.cpp +++ b/toolkit/components/windowwatcher/nsWindowWatcher.cpp @@ -295,7 +295,7 @@ nsWindowWatcher::OpenWindow(mozIDOMWindowProxy* aParent, const nsACString& aUrl, /* navigate = */ true, argv, /* aIsPopupSpam = */ false, /* aForceNoOpener = */ false, - /* aForceNoReferrer = */ false, + /* aForceNoReferrer = */ false, PRINT_NONE, /* aLoadState = */ nullptr, getter_AddRefs(bc))); if (bc) { nsCOMPtr win(bc->GetDOMWindow()); @@ -355,7 +355,7 @@ nsWindowWatcher::OpenWindow2(mozIDOMWindowProxy* aParent, bool aCalledFromScript, bool aDialog, bool aNavigate, nsISupports* aArguments, bool aIsPopupSpam, bool aForceNoOpener, - bool aForceNoReferrer, + bool aForceNoReferrer, PrintKind aPrintKind, nsDocShellLoadState* aLoadState, BrowsingContext** aResult) { nsCOMPtr argv = ConvertArgsToArray(aArguments); @@ -375,8 +375,8 @@ nsWindowWatcher::OpenWindow2(mozIDOMWindowProxy* aParent, return OpenWindowInternal(aParent, aUrl, aName, aFeatures, aCalledFromScript, dialog, aNavigate, argv, aIsPopupSpam, - aForceNoOpener, aForceNoReferrer, aLoadState, - aResult); + aForceNoOpener, aForceNoReferrer, aPrintKind, + aLoadState, aResult); } // This static function checks if the aDocShell uses an UserContextId equal to @@ -587,8 +587,8 @@ nsresult nsWindowWatcher::OpenWindowInternal( mozIDOMWindowProxy* aParent, const nsACString& aUrl, const nsACString& aName, const nsACString& aFeatures, bool aCalledFromJS, bool aDialog, bool aNavigate, nsIArray* aArgv, bool aIsPopupSpam, - bool aForceNoOpener, bool aForceNoReferrer, nsDocShellLoadState* aLoadState, - BrowsingContext** aResult) { + bool aForceNoOpener, bool aForceNoReferrer, PrintKind aPrintKind, + nsDocShellLoadState* aLoadState, BrowsingContext** aResult) { MOZ_ASSERT_IF(aForceNoReferrer, aForceNoOpener); nsresult rv = NS_OK; @@ -775,6 +775,8 @@ nsresult nsWindowWatcher::OpenWindowInternal( openWindowInfo = new nsOpenWindowInfo(); openWindowInfo->mForceNoOpener = aForceNoOpener; openWindowInfo->mParent = parentBC; + openWindowInfo->mIsForPrinting = aPrintKind != PRINT_NONE; + openWindowInfo->mIsForPrintPreview = aPrintKind == PRINT_PREVIEW; // We're going to want the window to be immediately available, meaning we // want it to match the current remoteness. @@ -2437,8 +2439,13 @@ void nsWindowWatcher::SizeOpenedWindow(nsIDocShellTreeOwner* aTreeOwner, int32_t nsWindowWatcher::GetWindowOpenLocation(nsPIDOMWindowOuter* aParent, uint32_t aChromeFlags, bool aCalledFromJS, - bool aWidthSpecified) { - bool isFullScreen = aParent->GetFullScreen(); + bool aWidthSpecified, + bool aIsForPrinting) { + // These windows are not actually visible to the user, so we return the thing + // that we can always handle. + if (aIsForPrinting) { + return nsIBrowserDOMWindow::OPEN_PRINT_BROWSER; + } // Where should we open this? int32_t containerPref; @@ -2449,8 +2456,9 @@ int32_t nsWindowWatcher::GetWindowOpenLocation(nsPIDOMWindowOuter* aParent, } bool isDisabledOpenNewWindow = - isFullScreen && Preferences::GetBool( - "browser.link.open_newwindow.disabled_in_fullscreen"); + aParent->GetFullScreen() && + Preferences::GetBool( + "browser.link.open_newwindow.disabled_in_fullscreen"); if (isDisabledOpenNewWindow && (containerPref == nsIBrowserDOMWindow::OPEN_NEWWINDOW)) { diff --git a/toolkit/components/windowwatcher/nsWindowWatcher.h b/toolkit/components/windowwatcher/nsWindowWatcher.h index 1fec97211fc9..2d647fb16005 100644 --- a/toolkit/components/windowwatcher/nsWindowWatcher.h +++ b/toolkit/components/windowwatcher/nsWindowWatcher.h @@ -54,8 +54,8 @@ class nsWindowWatcher : public nsIWindowWatcher, static int32_t GetWindowOpenLocation(nsPIDOMWindowOuter* aParent, uint32_t aChromeFlags, - bool aCalledFromJS, - bool aWidthSpecified); + bool aCalledFromJS, bool aWidthSpecified, + bool aIsForPrinting); // Will first look for a caller on the JS stack, and then fall back on // aCurrentContext if it can't find one. @@ -81,7 +81,7 @@ class nsWindowWatcher : public nsIWindowWatcher, const nsACString& aFeatures, bool aCalledFromJS, bool aDialog, bool aNavigate, nsIArray* aArgv, bool aIsPopupSpam, bool aForceNoOpener, - bool aForceNoReferrer, + bool aForceNoReferrer, PrintKind, nsDocShellLoadState* aLoadState, mozilla::dom::BrowsingContext** aResult); diff --git a/xpfe/appshell/nsContentTreeOwner.cpp b/xpfe/appshell/nsContentTreeOwner.cpp index 92dfb5370a26..1cf30413035f 100644 --- a/xpfe/appshell/nsContentTreeOwner.cpp +++ b/xpfe/appshell/nsContentTreeOwner.cpp @@ -604,7 +604,7 @@ nsContentTreeOwner::ProvideWindow( bool aCalledFromJS, bool aWidthSpecified, nsIURI* aURI, const nsAString& aName, const nsACString& aFeatures, bool aForceNoOpener, bool aForceNoReferrer, nsDocShellLoadState* aLoadState, bool* aWindowIsNew, - BrowsingContext** aReturn) { + dom::BrowsingContext** aReturn) { NS_ENSURE_ARG_POINTER(aOpenWindowInfo); RefPtr parent = aOpenWindowInfo->GetParent(); @@ -625,10 +625,12 @@ nsContentTreeOwner::ProvideWindow( #endif int32_t openLocation = nsWindowWatcher::GetWindowOpenLocation( - parent->GetDOMWindow(), aChromeFlags, aCalledFromJS, aWidthSpecified); + parent->GetDOMWindow(), aChromeFlags, aCalledFromJS, aWidthSpecified, + aOpenWindowInfo->GetIsForPrinting()); if (openLocation != nsIBrowserDOMWindow::OPEN_NEWTAB && - openLocation != nsIBrowserDOMWindow::OPEN_CURRENTWINDOW) { + openLocation != nsIBrowserDOMWindow::OPEN_CURRENTWINDOW && + openLocation != nsIBrowserDOMWindow::OPEN_PRINT_BROWSER) { // Just open a window normally return NS_OK; }