Skip to content

Commit

Permalink
Bug 1636728 - Centralize printing entry points in nsGlobalWindowOuter…
Browse files Browse the repository at this point in the history
…, 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
  • Loading branch information
emilio committed Aug 25, 2020
1 parent 666f7c3 commit f6f2d83
Show file tree
Hide file tree
Showing 40 changed files with 585 additions and 511 deletions.
26 changes: 20 additions & 6 deletions browser/base/content/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down
10 changes: 6 additions & 4 deletions browser/components/ssb/content/ssb.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand All @@ -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)) {
Expand Down
54 changes: 0 additions & 54 deletions docshell/base/BrowsingContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
7 changes: 0 additions & 7 deletions docshell/base/BrowsingContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down Expand Up @@ -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(); }

Expand Down Expand Up @@ -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.
Expand Down
39 changes: 0 additions & 39 deletions docshell/base/nsDocShell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 0 additions & 18 deletions docshell/base/nsIDocShell.idl
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
18 changes: 7 additions & 11 deletions dom/base/nsFrameLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
9 changes: 9 additions & 0 deletions dom/base/nsGlobalWindowInner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
4 changes: 4 additions & 0 deletions dom/base/nsGlobalWindowInner.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class nsIScriptTimeoutHandler;
class nsIBrowserChild;
class nsITimeoutHandler;
class nsIWebBrowserChrome;
class nsIWebProgressListener;
class mozIDOMWindowProxy;

class nsScreen;
Expand Down Expand Up @@ -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,
Expand Down
Loading

0 comments on commit f6f2d83

Please sign in to comment.