Bug 1636728 - Centralize printing entry points in nsGlobalWindowOuter, and move cloning out of nsPrintJob. r=jwatt,geckoview-reviewers,smaug,agi

This centralizes our print and preview setup in nsGlobalWindowOuter so
that we never re-clone a clone, and so that we reuse the window.open()
codepath to create the browsing context to clone into.

For window.print, for both old print dialog / silent printing and new
print preview UI, we now create a hidden browser (as in with visibility:
collapse, which takes no space but still gets a layout box).

 * In the modern UI case, this browser is swapped with the actual print
   preview clone, and the UI takes care of removing the browser.

 * In the print dialog / silent printing case, the printing code calls
   window.close() from nsDocumentViewer::OnDonePrinting().

 * We don't need to care about the old print preview UI for this case
   because it can't be open from window.print().

We need to fall back to an actual window when there's no
nsIBrowserDOMWindow around for WPT print tests and the like, which don't
have one. That seems fine, we could special-case this code path more if
needed but it doesn't seem worth it.

Differential Revision: https://phabricator.services.mozilla.com/D87063
This commit is contained in:
Emilio Cobos Álvarez 2020-08-25 17:45:12 +00:00
Родитель aaf9237935
Коммит 044b3c4332
40 изменённых файлов: 585 добавлений и 511 удалений

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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)) {

Просмотреть файл

@ -2033,60 +2033,6 @@ void BrowsingContext::DidSet(FieldIndex<IDX_IsActive>, 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<IDX_IsAwaitingPrint>, 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<Element> el = Top()->GetEmbedderElement();
if (NS_WARN_IF(!el)) {
return;
}
nsCOMPtr<nsPIDOMWindowOuter> outerWin = el->OwnerDoc()->GetWindow();
if (NS_WARN_IF(!outerWin)) {
return;
}
nsCOMPtr<nsIDOMChromeWindow> chromeWin = do_QueryInterface(outerWin);
if (NS_WARN_IF(!chromeWin)) {
return;
}
nsCOMPtr<nsIBrowserDOMWindow> 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<IDX_Muted>) {
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,

Просмотреть файл

@ -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<IDX_IsActive>, bool aOldValue);
void DidSet(FieldIndex<IDX_IsAwaitingPrint>, 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.

Просмотреть файл

@ -12407,47 +12407,8 @@ void nsDocShell::SetIsPrinting(bool aIsPrinting) {
mIsPrintingOrPP = aIsPrinting;
}
NS_IMETHODIMP
nsDocShell::InitOrReusePrintPreviewViewer(nsIWebBrowserPrint** aPrintPreview) {
*aPrintPreview = nullptr;
#if NS_PRINT_PREVIEW
nsCOMPtr<nsIDocumentViewerPrint> 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<nsIPrincipal> principal =
NullPrincipal::CreateWithInheritedAttributes(this);
nsCOMPtr<nsIURI> 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<nsIWebBrowserPrint> 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<nsIDocumentViewerPrint> vp = do_QueryInterface(mContentViewer);
MOZ_ASSERT(vp && vp->IsInitializedForPrintPreview());
# endif
nsCOMPtr<nsIWebBrowserPrint> viewer = do_QueryInterface(mContentViewer);
return viewer->ExitPrintPreview();
#else

Просмотреть файл

@ -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.

Просмотреть файл

@ -3210,23 +3210,19 @@ already_AddRefed<Promise> nsFrameLoader::Print(uint64_t aOuterWindowID,
return promise.forget();
}
nsGlobalWindowOuter* outerWindow =
RefPtr<nsGlobalWindowOuter> outerWindow =
nsGlobalWindowOuter::GetOuterWindowWithId(aOuterWindowID);
if (NS_WARN_IF(!outerWindow)) {
promise->MaybeReject(ErrorResult(NS_ERROR_FAILURE));
return promise.forget();
}
nsCOMPtr<nsIWebBrowserPrint> 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();

Просмотреть файл

@ -3605,6 +3605,15 @@ void nsGlobalWindowInner::Print(ErrorResult& aError) {
FORWARD_TO_OUTER_OR_THROW(PrintOuter, (aError), aError, );
}
Nullable<WindowProxyHolder> 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),

Просмотреть файл

@ -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<mozilla::dom::WindowProxyHolder> PrintPreview(
nsIPrintSettings*, nsIWebProgressListener*, nsIDocShell*,
mozilla::ErrorResult&);
void PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
const nsAString& aTargetOrigin,
const mozilla::dom::Sequence<JSObject*>& aTransfer,

Просмотреть файл

@ -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 <algorithm>
@ -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<nsCOMPtr<Document>>& 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<nsCOMPtr<Document>> targets;
CollectDocuments(aDoc, targets);
for (nsCOMPtr<Document>& 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<WindowProxyHolder> nsGlobalWindowOuter::Print(
nsIPrintSettings* aPrintSettings, nsIWebProgressListener* aListener,
nsIDocShell* aDocShellToCloneInto, bool aIsPreview, ErrorResult& aError) {
#ifdef NS_PRINTING
nsCOMPtr<nsIPrintSettingsService> 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<Document> 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<BrowsingContext> 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<BrowsingContext> bc = GetBrowsingContext();
if (!bc || bc->IsDiscarded()) {
return;
nsCOMPtr<nsIContentViewer> cv;
RefPtr<BrowsingContext> 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<nsIDocShell> 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<nsIPrintSettingsService> 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<nsIDocShell> 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<nsIWebBrowserPrint> webBrowserPrint =
do_GetInterface(static_cast<nsIInterfaceRequestor*>(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<Document> 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<Document> 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> element = do_QueryObject(clone.mElement);
RefPtr<nsFrameLoader> frameLoader =
nsFrameLoader::Create(element, /* aNetworkCreated */ false);
if (NS_WARN_IF(!frameLoader)) {
continue;
}
clone.mElement->SetFrameLoader(frameLoader);
nsCOMPtr<nsIDocShell> docshell;
RefPtr<Document> 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<nsIWebBrowserPrint> 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<WindowProxyHolder> nsGlobalWindowOuter::OpenDialogOuter(
@ -5712,7 +5882,7 @@ Nullable<WindowProxyHolder> 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));
}
}

Просмотреть файл

@ -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<mozilla::dom::WindowProxyHolder> Print(
nsIPrintSettings*, nsIWebProgressListener*, nsIDocShell*, bool aIsPreview,
mozilla::ErrorResult&);
mozilla::dom::Selection* GetSelectionOuter();
already_AddRefed<mozilla::dom::Selection> 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:

Просмотреть файл

@ -96,8 +96,6 @@ interface BrowsingContext {
[SetterThrows] attribute float textZoom;
[SetterThrows] attribute boolean isAwaitingPrint;
/**
* Whether this docshell should save entries in global history.
*/

Просмотреть файл

@ -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

Просмотреть файл

@ -836,7 +836,8 @@ BrowserChild::ProvideWindow(nsIOpenWindowInfo* aOpenWindowInfo,
RefPtr<BrowsingContext> 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> 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<nsGlobalWindowOuter> outerWindow =
nsGlobalWindowOuter::GetOuterWindowWithId(aOuterWindowID);
if (NS_WARN_IF(!outerWindow)) {
return IPC_OK();
}
nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint =
do_GetInterface(ToSupports(outerWindow));
if (NS_WARN_IF(!webBrowserPrint)) {
return IPC_OK();
}
nsCOMPtr<nsIPrintSettingsService> 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();
}

Просмотреть файл

@ -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));

Просмотреть файл

@ -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<nsIRemoteTab>& 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<BrowserParent> 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<Element> openerElement = do_QueryObject(frame);
nsCOMPtr<nsIOpenURIInFrameParams> params =
@ -5144,7 +5152,8 @@ mozilla::ipc::IPCResult ContentParent::CommonCreateWindow(
mozilla::ipc::IPCResult ContentParent::RecvCreateWindow(
PBrowserParent* aThisTab, const MaybeDiscarded<BrowsingContext>& 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);

Просмотреть файл

@ -530,7 +530,8 @@ class ContentParent final
PBrowserParent* aThisBrowserParent,
const MaybeDiscarded<BrowsingContext>& 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<nsIRemoteTab>& aNewRemoteTab,

Просмотреть файл

@ -1441,6 +1441,8 @@ parent:
uint32_t aChromeFlags,
bool aCalledFromJS,
bool aWidthSpecified,
bool aForPrinting,
bool aForPrintPreview,
nsIURI aURIToLoad,
nsCString aFeatures,
float aFullZoom,

Просмотреть файл

@ -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<object> transfer = []);

Просмотреть файл

@ -162,8 +162,7 @@ nsresult XULFrameElement::BindToTree(BindContext& aContext, nsINode& aParent) {
}
void XULFrameElement::UnbindFromTree(bool aNullParent) {
RefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
if (frameLoader) {
if (RefPtr<nsFrameLoader> frameLoader = GetFrameLoader()) {
frameLoader->Destroy();
}
mFrameLoader = nullptr;

Просмотреть файл

@ -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<Document> 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<nsPIDOMWindowOuter> window = do_QueryInterface(aChildDOMWin);
MOZ_ASSERT(window);
nsCOMPtr<Document> 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 <browser> 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<nsPIDOMWindowOuter> 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) {

Просмотреть файл

@ -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;

Просмотреть файл

@ -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() {

Просмотреть файл

@ -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 = "<div></div>";
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 <use> 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`);

Просмотреть файл

@ -173,27 +173,6 @@ static void DumpPrintObjectsTreeLayout(const UniquePtr<nsPrintObject>& aPO,
# define DUMP_DOC_TREELAYOUT
#endif
static CallState CollectDocuments(Document& aDoc,
nsTArray<nsCOMPtr<Document>>& 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<nsCOMPtr<Document>> targets;
CollectDocuments(aDoc, targets);
for (nsCOMPtr<Document>& doc : targets) {
nsContentUtils::DispatchTrustedEvent(doc, doc->GetWindow(), aEvent,
CanBubble::eNo, Cancelable::eNo,
nullptr);
}
}
// -------------------------------------------------------
// Helpers
// -------------------------------------------------------
@ -296,39 +275,12 @@ static void BuildNestedPrintObjects(const UniquePtr<nsPrintObject>& aParentPO,
aPrintData->mSelectionRoot = aPrintData->mPrintObject.get();
}
nsTArray<Document::PendingFrameStaticClone> pendingClones =
aParentPO->mDocument->TakePendingFrameStaticClones();
for (auto& clone : pendingClones) {
if (NS_WARN_IF(!clone.mStaticCloneOf)) {
continue;
}
RefPtr<Element> element = do_QueryObject(clone.mElement);
RefPtr<nsFrameLoader> frameLoader =
nsFrameLoader::Create(element, /* aNetworkCreated */ false);
clone.mElement->SetFrameLoader(frameLoader);
nsCOMPtr<nsIDocShell> docshell;
RefPtr<Document> 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<nsIDocShell> docShell = bc->GetDocShell();
RefPtr<Document> doc = docShell->GetDocument();
auto childPO = MakeUnique<nsPrintObject>();
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<Document> focusedDoc = FindFocusedDocument();
RefPtr<Document> focusedDoc = FindFocusedDocument(aDoc);
// Get the docshell for this documentviewer
nsCOMPtr<nsIDocShell> 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<nsPrintObject>();
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<nsIDocShell> 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<nsIPrintSettings> 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<nsIWebProgressListener> 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<nsPIDOMWindowOuter> rootWindow = window->GetPrivateRoot();

Просмотреть файл

@ -178,7 +178,7 @@ class nsPrintJob final : public nsIObserver,
nsresult ReflowPrintObject(const mozilla::UniquePtr<nsPrintObject>& 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<nsPrintObject>& 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 };

Просмотреть файл

@ -62,59 +62,13 @@ nsresult nsPrintObject::InitAsRootObject(nsIDocShell* aDocShell, Document* aDoc,
NS_ENSURE_STATE(aDocShell);
NS_ENSURE_STATE(aDoc);
if (aForPrintPreview) {
nsCOMPtr<nsIContentViewer> 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.
MOZ_ASSERT(aDoc->IsStaticDocument());
// Create a new BrowsingContext to create our DocShell in.
RefPtr<BrowsingContext> bc = BrowsingContext::CreateIndependent(
nsDocShell::Cast(aDocShell)->GetBrowsingContext()->GetType());
mDocShell = aDocShell;
mDocument = aDoc;
// 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();
}
// 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);
nsCOMPtr<nsIContentViewer> 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> presShell = mPresShell;
mPresShell = nullptr;
presShell->Destroy();
if (mDocument) {
if (RefPtr<PresShell> ps = mDocument->GetPresShell()) {
MOZ_DIAGNOSTIC_ASSERT(!mPresShell || ps == mPresShell);
mPresShell = nullptr;
nsAutoScriptBlocker scriptBlocker;
ps->EndObservingDocument();
ps->Destroy();
}
}
mPresShell = nullptr;
mPresContext = nullptr;
mViewManager = nullptr;
}

Просмотреть файл

@ -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(

Просмотреть файл

@ -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);
}
}

Просмотреть файл

@ -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

Просмотреть файл

@ -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;
},

Просмотреть файл

@ -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 <browser>.
return browser;
}
this.printWindow(aBrowsingContext, null);
return null;
},
/**

Просмотреть файл

@ -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,

Просмотреть файл

@ -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();

Просмотреть файл

@ -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;

Просмотреть файл

@ -20,6 +20,8 @@ class nsOpenWindowInfo : public nsIOpenWindowInfo {
bool mForceNoOpener = false;
bool mIsRemote = false;
bool mIsForPrinting = false;
bool mIsForPrintPreview = false;
RefPtr<mozilla::dom::BrowserParent> mNextRemoteBrowser;
mozilla::OriginAttributes mOriginAttributes;
RefPtr<mozilla::dom::BrowsingContext> mParent;

Просмотреть файл

@ -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);
/**

Просмотреть файл

@ -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<mozIDOMWindowProxy> 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<nsIArray> 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)) {

Просмотреть файл

@ -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);

Просмотреть файл

@ -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<dom::BrowsingContext> 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;
}