Bug 1141337 - Update nsIWebBrowserPersistable to allow subframe targeting for Save Frame As. r=jld

This alters nsIWebBrowserPersistable so that startPersistence takes an
outerWindowID. This allows us to target a particular subframe from
beneath an nsFrameLoader, which is useful when attempting to Save
Frame As a remote browser.
This commit is contained in:
Mike Conley 2015-08-06 10:44:16 -04:00
parent 6e7ec6c889
commit cfd5c1fa5a
13 changed files with 113 additions and 26 deletions

View File

@ -1186,7 +1186,7 @@ nsContextMenu.prototype = {
// Save URL of clicked-on frame.
saveFrame: function () {
saveDocument(this.target.ownerDocument);
saveBrowser(this.browser, false, this.frameOuterWindowID);
},
// Helper function to wait for appropriate MIME-type headers and

View File

@ -3849,6 +3849,30 @@ nsContentUtils::MatchElementId(nsIContent *aContent, const nsAString& aId)
return MatchElementId(aContent, id);
}
/* static */
nsIDocument*
nsContentUtils::GetSubdocumentWithOuterWindowId(nsIDocument *aDocument,
uint64_t aOuterWindowId)
{
if (!aDocument || !aOuterWindowId) {
return nullptr;
}
nsCOMPtr<nsPIDOMWindow> window = nsGlobalWindow::GetOuterWindowWithId(aOuterWindowId);
if (!window) {
return nullptr;
}
nsCOMPtr<nsIDocument> foundDoc = window->GetDoc();
if (nsContentUtils::ContentIsCrossDocDescendantOf(foundDoc, aDocument)) {
// Note that ContentIsCrossDocDescendantOf will return true if
// foundDoc == aDocument.
return foundDoc;
}
return nullptr;
}
// Convert the string from the given encoding to Unicode.
/* static */
nsresult

View File

@ -325,6 +325,23 @@ public:
*/
static uint16_t ReverseDocumentPosition(uint16_t aDocumentPosition);
/**
* Returns a subdocument for aDocument with a particular outer window ID.
*
* @param aDocument
* The document whose subdocuments will be searched.
* @param aOuterWindowID
* The outer window ID for the subdocument to be found. This must
* be a value greater than 0.
* @return nsIDocument*
* A pointer to the found nsIDocument. nullptr if the subdocument
* cannot be found, or if either aDocument or aOuterWindowId were
* invalid. If the outer window ID belongs to aDocument itself, this
* will return a pointer to aDocument.
*/
static nsIDocument* GetSubdocumentWithOuterWindowId(nsIDocument *aDocument,
uint64_t aOuterWindowId);
static uint32_t CopyNewlineNormalizedUnicodeTo(const nsAString& aSource,
uint32_t aSrcOffset,
char16_t* aDest,

View File

@ -2885,18 +2885,31 @@ nsFrameLoader::InitializeBrowserAPI()
}
NS_IMETHODIMP
nsFrameLoader::StartPersistence(nsIWebBrowserPersistDocumentReceiver* aRecv)
nsFrameLoader::StartPersistence(uint64_t aOuterWindowID,
nsIWebBrowserPersistDocumentReceiver* aRecv)
{
if (!aRecv) {
return NS_ERROR_INVALID_POINTER;
}
if (mRemoteBrowser) {
return mRemoteBrowser->StartPersistence(aRecv);
return mRemoteBrowser->StartPersistence(aOuterWindowID, aRecv);
}
if (mDocShell) {
nsCOMPtr<nsIDocument> doc = do_GetInterface(mDocShell);
NS_ENSURE_STATE(doc);
nsCOMPtr<nsIDocument> rootDoc = do_GetInterface(mDocShell);
nsCOMPtr<nsIDocument> foundDoc;
if (aOuterWindowID) {
foundDoc = nsContentUtils::GetSubdocumentWithOuterWindowId(rootDoc, aOuterWindowID);
} else {
foundDoc = rootDoc;
}
if (!foundDoc) {
aRecv->OnError(NS_ERROR_NO_CONTENT);
} else {
nsCOMPtr<nsIWebBrowserPersistDocument> pdoc =
new mozilla::WebBrowserPersistLocalDocument(doc);
new mozilla::WebBrowserPersistLocalDocument(foundDoc);
aRecv->OnDocumentReady(pdoc);
return NS_OK;
}
return NS_ERROR_NO_CONTENT;
return NS_OK;
}

View File

@ -121,7 +121,7 @@ both:
*/
PRenderFrame();
PWebBrowserPersistDocument();
PWebBrowserPersistDocument(uint64_t aOuterWindowID);
parent:
/**
@ -524,6 +524,7 @@ parent:
child:
NativeSynthesisResponse(uint64_t aObserverId, nsCString aResponse);
parent:
/**

View File

@ -3118,16 +3118,28 @@ TabChildGlobal::GetGlobalJSObject()
}
PWebBrowserPersistDocumentChild*
TabChild::AllocPWebBrowserPersistDocumentChild()
TabChild::AllocPWebBrowserPersistDocumentChild(const uint64_t& aOuterWindowID)
{
return new WebBrowserPersistDocumentChild();
}
bool
TabChild::RecvPWebBrowserPersistDocumentConstructor(PWebBrowserPersistDocumentChild *aActor)
TabChild::RecvPWebBrowserPersistDocumentConstructor(PWebBrowserPersistDocumentChild *aActor,
const uint64_t& aOuterWindowID)
{
nsCOMPtr<nsIDocument> doc = GetDocument();
static_cast<WebBrowserPersistDocumentChild*>(aActor)->Start(doc);
nsCOMPtr<nsIDocument> rootDoc = GetDocument();
nsCOMPtr<nsIDocument> foundDoc;
if (aOuterWindowID) {
foundDoc = nsContentUtils::GetSubdocumentWithOuterWindowId(rootDoc, aOuterWindowID);
} else {
foundDoc = rootDoc;
}
if (!foundDoc) {
aActor->SendInitFailure(NS_ERROR_NO_CONTENT);
} else {
static_cast<WebBrowserPersistDocumentChild*>(aActor)->Start(foundDoc);
}
return true;
}

View File

@ -495,8 +495,9 @@ public:
virtual ScreenIntSize GetInnerSize() override;
virtual PWebBrowserPersistDocumentChild* AllocPWebBrowserPersistDocumentChild() override;
virtual bool RecvPWebBrowserPersistDocumentConstructor(PWebBrowserPersistDocumentChild *aActor) override;
virtual PWebBrowserPersistDocumentChild* AllocPWebBrowserPersistDocumentChild(const uint64_t& aOuterWindowID) override;
virtual bool RecvPWebBrowserPersistDocumentConstructor(PWebBrowserPersistDocumentChild *aActor,
const uint64_t& aOuterWindowID) override;
virtual bool DeallocPWebBrowserPersistDocumentChild(PWebBrowserPersistDocumentChild* aActor) override;
protected:

View File

@ -3368,7 +3368,7 @@ TabParent::AsyncPanZoomEnabled() const
}
PWebBrowserPersistDocumentParent*
TabParent::AllocPWebBrowserPersistDocumentParent()
TabParent::AllocPWebBrowserPersistDocumentParent(const uint64_t& aOuterWindowID)
{
return new WebBrowserPersistDocumentParent();
}
@ -3381,11 +3381,12 @@ TabParent::DeallocPWebBrowserPersistDocumentParent(PWebBrowserPersistDocumentPar
}
NS_IMETHODIMP
TabParent::StartPersistence(nsIWebBrowserPersistDocumentReceiver* aRecv)
TabParent::StartPersistence(uint64_t aOuterWindowID,
nsIWebBrowserPersistDocumentReceiver* aRecv)
{
auto* actor = new WebBrowserPersistDocumentParent();
actor->SetOnReady(aRecv);
return SendPWebBrowserPersistDocumentConstructor(actor)
return SendPWebBrowserPersistDocumentConstructor(actor, aOuterWindowID)
? NS_OK : NS_ERROR_FAILURE;
// (The actor will be destroyed on constructor failure.)
}

View File

@ -435,7 +435,7 @@ public:
int32_t& aDragAreaX, int32_t& aDragAreaY);
layout::RenderFrameParent* GetRenderFrame();
virtual PWebBrowserPersistDocumentParent* AllocPWebBrowserPersistDocumentParent() override;
virtual PWebBrowserPersistDocumentParent* AllocPWebBrowserPersistDocumentParent(const uint64_t& aOuterWindowID) override;
virtual bool DeallocPWebBrowserPersistDocumentParent(PWebBrowserPersistDocumentParent* aActor) override;
protected:

View File

@ -346,7 +346,10 @@ ResourceReader::OnWalkSubframe(nsIDOMNode* aNode)
NS_ENSURE_STATE(loader);
++mOutstandingDocuments;
nsresult rv = loader->StartPersistence(this);
// Pass in 0 as the outer window ID so that we start
// persisting the root of this subframe, and not some other
// subframe child of this subframe.
nsresult rv = loader->StartPersistence(0, this);
if (NS_FAILED(rv)) {
if (rv == NS_ERROR_NO_CONTENT) {
// Just ignore frames with no content document.

View File

@ -37,7 +37,11 @@ WebBrowserPersistResourcesChild::VisitDocument(nsIWebBrowserPersistDocument* aDo
{
auto* subActor = new WebBrowserPersistDocumentChild();
dom::PBrowserChild* grandManager = Manager()->Manager();
if (!grandManager->SendPWebBrowserPersistDocumentConstructor(subActor)) {
// As a consequence of how PWebBrowserPersistDocumentConstructor can be
// sent by both the parent and the child, we must pass the outerWindowID
// argument here. We pass 0, though note that this argument is actually
// just ignored when passed up to the parent from the child.
if (!grandManager->SendPWebBrowserPersistDocumentConstructor(subActor, 0)) {
// NOTE: subActor is freed at this point.
return NS_ERROR_FAILURE;
}

View File

@ -22,9 +22,20 @@ interface nsIWebBrowserPersistDocumentReceiver;
* @see nsIWebBrowserPersistDocumentReceiver
* @see nsIWebBrowserPersistDocument
* @see nsIWebBrowserPersist
*
* @param aOuterWindowID
* The outer window ID of the subframe we'd like to persist.
* If set at 0, nsIWebBrowserPersistable will attempt to persist
* the top-level document. If the outer window ID is for a subframe
* that does not exist, or is not held beneath the nsIWebBrowserPersistable,
* aRecv's onError method will be called with NS_ERROR_NO_CONTENT.
* @param aRecv
* The nsIWebBrowserPersistDocumentReceiver is a callback that
* will be fired once the document is ready for persisting.
*/
[scriptable, function, uuid(24d0dc9e-b970-4cca-898f-cbba03abaa73)]
[scriptable, uuid(f4c3fa8e-83e9-49f8-ac6f-951fc7541fe4)]
interface nsIWebBrowserPersistable : nsISupports
{
void startPersistence(in nsIWebBrowserPersistDocumentReceiver aRecv);
void startPersistence(in unsigned long long aOuterWindowID,
in nsIWebBrowserPersistDocumentReceiver aRecv);
};

View File

@ -129,7 +129,7 @@ function saveImageURL(aURL, aFileName, aFilePickerTitleKey, aShouldBypassCache,
// This is like saveDocument, but takes any browser/frame-like element
// (nsIFrameLoaderOwner) and saves the current document inside it,
// whether in-process or out-of-process.
function saveBrowser(aBrowser, aSkipPrompt)
function saveBrowser(aBrowser, aSkipPrompt, aOuterWindowID=0)
{
if (!aBrowser) {
throw "Must have a browser when calling saveBrowser";
@ -138,7 +138,7 @@ function saveBrowser(aBrowser, aSkipPrompt)
.frameLoader
.QueryInterface(Ci.nsIWebBrowserPersistable);
let stack = Components.stack.caller;
persistable.startPersistence({
persistable.startPersistence(aOuterWindowID, {
onDocumentReady: function (document) {
saveDocument(document, aSkipPrompt);
},