Bug 767638 - Greatly simplify loading full-page plugins. r=josh

This commit is contained in:
John Schoenick 2012-12-10 16:35:05 -08:00
parent 2acf626c29
commit 84b9c456d3
10 changed files with 85 additions and 230 deletions

View File

@ -5,6 +5,7 @@
#include "nsISupports.idl"
interface nsIRequest;
interface nsIFrame;
interface nsIObjectFrame;
interface nsIPluginTag;
@ -21,7 +22,7 @@ interface nsIURI;
* This interface represents a content node that loads objects.
*/
[scriptable, uuid(649b8a75-8623-437f-ad30-0ac596c8452e)]
[scriptable, uuid(e9102696-4bb4-4a98-a31f-be9da5a3d18e)]
interface nsIObjectLoadingContent : nsISupports
{
/**
@ -130,6 +131,19 @@ interface nsIObjectLoadingContent : nsISupports
[noscript] void syncStartPluginInstance();
[noscript] void asyncStartPluginInstance();
/**
* Puts the tag in the "waiting on a channel" state and adopts this
* channel. This does not override the normal logic of examining attributes
* and the channel type, so the load may cancel this channel if it decides not
* to use one.
*
* This assumes:
* - This tag has not begun loading yet
* - This channel has not yet hit OnStartRequest
* - The caller will continue to pass channel events to us as a listener
*/
[noscript] void initializeFromChannel(in nsIRequest request);
/**
* Requests the plugin instance for scripting, attempting to spawn it if
* appropriate.

View File

@ -22,7 +22,6 @@
#include "nsIExternalProtocolHandler.h"
#include "nsEventStates.h"
#include "nsIObjectFrame.h"
#include "nsIPluginDocument.h"
#include "nsIPermissionManager.h"
#include "nsPluginHost.h"
#include "nsIPresShell.h"
@ -741,26 +740,9 @@ nsObjectLoadingContent::InstantiatePluginInstance(bool aIsLoading)
appShell->SuspendNative();
}
nsCOMPtr<nsIPluginDocument> pDoc(do_QueryInterface(doc));
bool fullPageMode = false;
if (pDoc) {
pDoc->GetWillHandleInstantiation(&fullPageMode);
}
if (fullPageMode) {
nsCOMPtr<nsIStreamListener> stream;
rv = pluginHost->InstantiateFullPagePluginInstance(mContentType.get(),
mURI.get(), this,
getter_AddRefs(mInstanceOwner),
getter_AddRefs(stream));
if (NS_SUCCEEDED(rv)) {
pDoc->SetStreamListener(stream);
}
} else {
rv = pluginHost->InstantiateEmbeddedPluginInstance(mContentType.get(),
mURI.get(), this,
getter_AddRefs(mInstanceOwner));
}
rv = pluginHost->InstantiateEmbeddedPluginInstance(mContentType.get(),
mURI.get(), this,
getter_AddRefs(mInstanceOwner));
if (appShell) {
appShell->ResumeNative();
@ -1575,6 +1557,34 @@ nsObjectLoadingContent::UpdateObjectParameters()
return retval;
}
// Used by PluginDocument to kick off our initial load from the already-opened
// channel.
NS_IMETHODIMP
nsObjectLoadingContent::InitializeFromChannel(nsIRequest *aChannel)
{
LOG(("OBJLC [%p] InitializeFromChannel: %p", this, aChannel));
if (mType != eType_Loading || mChannel) {
// We could technically call UnloadObject() here, if consumers have a valid
// reason for wanting to call this on an already-loaded tag.
NS_NOTREACHED("Should not have begun loading at this point");
return NS_ERROR_UNEXPECTED;
}
// Because we didn't open this channel from an initial LoadObject, we'll
// update our parameters now, so the OnStartRequest->LoadObject doesn't
// believe our src/type suddenly changed.
UpdateObjectParameters();
// But we always want to load from a channel, in this case.
mType = eType_Loading;
mChannel = do_QueryInterface(aChannel);
NS_ASSERTION(mChannel, "passed a request that is not a channel");
// OnStartRequest will now see we have a channel in the loading state, and
// call into LoadObject. There's a possibility LoadObject will decide not to
// load anything from a channel - it will call CloseChannel() in that case.
return NS_OK;
}
// Only OnStartRequest should be passing the channel parameter
nsresult
nsObjectLoadingContent::LoadObject(bool aNotify,

View File

@ -13,6 +13,7 @@
#include "nsGkAtoms.h"
#include "nsError.h"
#include "nsIDocument.h"
#include "nsIPluginDocument.h"
#include "nsIDOMDocument.h"
#include "nsIDOMSVGDocument.h"
#include "nsIDOMGetSVGDocument.h"
@ -241,8 +242,13 @@ nsHTMLObjectElement::BindToTree(nsIDocument *aDocument,
aCompileEventHandlers);
NS_ENSURE_SUCCESS(rv, rv);
// Don't kick off load from being bound to a plugin document - the plugin
// document will call nsObjectLoadingContent::InitializeFromChannel() for the
// initial load.
nsCOMPtr<nsIPluginDocument> pluginDoc = do_QueryInterface(aDocument);
// If we already have all the children, start the load.
if (mIsDoneAddingChildren) {
if (mIsDoneAddingChildren && !pluginDoc) {
void (nsHTMLObjectElement::*start)() = &nsHTMLObjectElement::StartObjectLoad;
nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this, start));
}

View File

@ -11,6 +11,7 @@
#include "nsGkAtoms.h"
#include "nsError.h"
#include "nsIDocument.h"
#include "nsIPluginDocument.h"
#include "nsIDOMDocument.h"
#include "nsIDOMHTMLAppletElement.h"
#include "nsIDOMHTMLEmbedElement.h"
@ -269,8 +270,13 @@ nsHTMLSharedObjectElement::BindToTree(nsIDocument *aDocument,
aCompileEventHandlers);
NS_ENSURE_SUCCESS(rv, rv);
// Don't kick off load from being bound to a plugin document - the plugin
// document will call nsObjectLoadingContent::InitializeFromChannel() for the
// initial load.
nsCOMPtr<nsIPluginDocument> pluginDoc = do_QueryInterface(aDocument);
// If we already have all the children, start the load.
if (mIsDoneAddingChildren) {
if (mIsDoneAddingChildren && !pluginDoc) {
void (nsHTMLSharedObjectElement::*start)() =
&nsHTMLSharedObjectElement::StartObjectLoad;
nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this, start));

View File

@ -45,10 +45,6 @@ public:
const nsCString& GetType() const { return mMimeType; }
nsIContent* GetPluginContent() { return mPluginContent; }
void AllowNormalInstantiation() {
mWillHandleInstantiation = false;
}
void StartLayout() { MediaDocument::StartLayout(); }
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PluginDocument, MediaDocument)
@ -58,11 +54,6 @@ protected:
nsCOMPtr<nsIContent> mPluginContent;
nsRefPtr<MediaDocumentStreamListener> mStreamListener;
nsCString mMimeType;
// Hack to handle the fact that plug-in loading lives in frames and that the
// frames may not be around when we need to instantiate. Once plug-in
// loading moves to content, this can all go away.
bool mWillHandleInstantiation;
};
class PluginStreamListener : public MediaDocumentStreamListener
@ -74,8 +65,6 @@ public:
{}
NS_IMETHOD OnStartRequest(nsIRequest* request, nsISupports *ctxt);
private:
nsresult SetupPlugin();
nsRefPtr<PluginDocument> mPluginDoc;
};
@ -84,66 +73,39 @@ NS_IMETHODIMP
PluginStreamListener::OnStartRequest(nsIRequest* request, nsISupports *ctxt)
{
SAMPLE_LABEL("PluginStreamListener", "OnStartRequest");
// Have to set up our plugin stuff before we call OnStartRequest, so
// that the plugin listener can get that call.
nsresult rv = SetupPlugin();
NS_ASSERTION(NS_FAILED(rv) || mNextStream,
"We should have a listener by now");
nsresult rv2 = MediaDocumentStreamListener::OnStartRequest(request, ctxt);
return NS_SUCCEEDED(rv) ? rv2 : rv;
}
nsresult
PluginStreamListener::SetupPlugin()
{
NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE);
mPluginDoc->StartLayout();
nsCOMPtr<nsIContent> embed = mPluginDoc->GetPluginContent();
nsCOMPtr<nsIObjectLoadingContent> objlc = do_QueryInterface(embed);
nsCOMPtr<nsIStreamListener> objListener = do_QueryInterface(objlc);
// Now we have a frame for our <embed>, start the load
nsCOMPtr<nsIPresShell> shell = mDocument->GetShell();
if (!shell) {
// Can't instantiate w/o a shell
mPluginDoc->AllowNormalInstantiation();
if (!objListener) {
NS_NOTREACHED("PluginStreamListener without appropriate content node");
return NS_BINDING_ABORTED;
}
// Flush out layout before we go to instantiate, because some
// plug-ins depend on NPP_SetWindow() being called early enough and
// nsObjectFrame does that at the end of reflow.
shell->FlushPendingNotifications(Flush_Layout);
SetStreamListener(objListener);
nsCOMPtr<nsIObjectLoadingContent> olc(do_QueryInterface(embed));
if (!olc) {
return NS_ERROR_UNEXPECTED;
}
nsObjectLoadingContent* olcc = static_cast<nsObjectLoadingContent*>(olc.get());
nsresult rv = olcc->InstantiatePluginInstance();
// Sets up the ObjectLoadingContent tag as if it is waiting for a
// channel, so it can proceed with a load normally once it gets OnStartRequest
nsresult rv = objlc->InitializeFromChannel(request);
if (NS_FAILED(rv)) {
NS_NOTREACHED("InitializeFromChannel failed");
return rv;
}
// Now that we're done, allow normal instantiation in the future
// (say if there's a reframe of this entire presentation).
mPluginDoc->AllowNormalInstantiation();
return NS_OK;
// Note that because we're now hooked up to a plugin listener, this will
// likely spawn a plugin, which may re-enter.
return MediaDocumentStreamListener::OnStartRequest(request, ctxt);
}
// NOTE! nsDocument::operator new() zeroes out all members, so don't
// bother initializing members to 0.
PluginDocument::PluginDocument()
: mWillHandleInstantiation(true)
{
}
{}
PluginDocument::~PluginDocument()
{
}
{}
NS_IMPL_CYCLE_COLLECTION_CLASS(PluginDocument)
@ -226,6 +188,8 @@ PluginDocument::StartDocumentLoad(const char* aCommand,
return rv;
}
MediaDocument::UpdateTitleAndCharset(mMimeType);
mStreamListener = new PluginStreamListener(this);
if (!mStreamListener) {
return NS_ERROR_OUT_OF_MEMORY;
@ -290,8 +254,8 @@ PluginDocument::CreateSyntheticPluginDocument()
mPluginContent->SetAttr(kNameSpaceID_None, nsGkAtoms::type,
NS_ConvertUTF8toUTF16(mMimeType), false);
// This will not start the load because nsObjectLoadingContent checks whether
// its document is an nsIPluginDocument
// nsHTML(Shared)ObjectElement does not kick off a load on BindToTree if it is
// to a PluginDocument
body->AppendChildTo(mPluginContent, false);
return NS_OK;
@ -299,18 +263,6 @@ PluginDocument::CreateSyntheticPluginDocument()
}
NS_IMETHODIMP
PluginDocument::SetStreamListener(nsIStreamListener *aListener)
{
if (mStreamListener) {
mStreamListener->SetStreamListener(aListener);
}
MediaDocument::UpdateTitleAndCharset(mMimeType);
return NS_OK;
}
NS_IMETHODIMP
PluginDocument::Print()
{
@ -335,13 +287,6 @@ PluginDocument::Print()
return NS_OK;
}
NS_IMETHODIMP
PluginDocument::GetWillHandleInstantiation(bool* aWillHandle)
{
*aWillHandle = mWillHandleInstantiation;
return NS_OK;
}
} // namespace dom
} // namespace mozilla

View File

@ -6,24 +6,11 @@
#include "nsISupports.idl"
#include "nsIStreamListener.idl"
[uuid(e4be1d0a-9f24-4d69-bec5-245726ab85fb)]
[uuid(a93a0f0f-24f0-4206-a21b-56a43dcbdd88)]
interface nsIPluginDocument : nsISupports
{
/**
* Sets the stream listener for this plugin document
*/
void setStreamListener(in nsIStreamListener aStreamListener);
/**
* Causes the plugin to print in full-page mode
*/
void print();
/**
* Check whether the document is planning to handle plug-in instantiation
* itself. If not, then the plugin content node should do it.
*/
// XXXbz once we move plug-in loading to content, this can go away.
readonly attribute boolean willHandleInstantiation;
};

View File

@ -994,69 +994,6 @@ nsPluginHost::InstantiateEmbeddedPluginInstance(const char *aMimeType, nsIURI* a
return NS_OK;
}
nsresult nsPluginHost::InstantiateFullPagePluginInstance(const char *aMimeType,
nsIURI* aURI,
nsObjectLoadingContent *aContent,
nsPluginInstanceOwner **aOwner,
nsIStreamListener **aStreamListener)
{
#ifdef PLUGIN_LOGGING
nsAutoCString urlSpec;
aURI->GetSpec(urlSpec);
PLUGIN_LOG(PLUGIN_LOG_NORMAL,
("nsPluginHost::InstantiateFullPagePlugin Begin mime=%s, url=%s\n",
aMimeType, urlSpec.get()));
#endif
nsRefPtr<nsPluginInstanceOwner> instanceOwner = new nsPluginInstanceOwner();
if (!instanceOwner) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsCOMPtr<nsIContent> ourContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(aContent));
nsresult rv = instanceOwner->Init(ourContent);
if (NS_FAILED(rv)) {
return rv;
}
rv = SetUpPluginInstance(aMimeType, aURI, instanceOwner);
if (NS_FAILED(rv)) {
return rv;
}
nsRefPtr<nsNPAPIPluginInstance> instance;
instanceOwner->GetInstance(getter_AddRefs(instance));
if (!instance) {
return NS_ERROR_FAILURE;
}
NPWindow* win = nullptr;
instanceOwner->GetWindow(win);
if (!win) {
return NS_ERROR_FAILURE;
}
// Set up any widget that might be required.
instanceOwner->CreateWidget();
instanceOwner->CallSetWindow();
rv = NewFullPagePluginStreamListener(aURI, instance.get(), aStreamListener);
if (NS_FAILED(rv)) {
return rv;
}
// Call SetWindow again in case something changed.
instanceOwner->CallSetWindow();
instanceOwner.forget(aOwner);
PLUGIN_LOG(PLUGIN_LOG_NORMAL,
("nsPluginHost::InstantiateFullPagePlugin End mime=%s, rv=%d, url=%s\n",
aMimeType, rv, urlSpec.get()));
return NS_OK;
}
nsPluginTag*
nsPluginHost::FindTagForLibrary(PRLibrary* aLibrary)
{
@ -3275,24 +3212,6 @@ nsresult nsPluginHost::NewEmbeddedPluginStreamListener(nsIURI* aURI,
return NS_OK;
}
nsresult nsPluginHost::NewFullPagePluginStreamListener(nsIURI* aURI,
nsNPAPIPluginInstance *aInstance,
nsIStreamListener **aStreamListener)
{
NS_ENSURE_ARG_POINTER(aURI);
NS_ENSURE_ARG_POINTER(aStreamListener);
nsRefPtr<nsPluginStreamListenerPeer> listener = new nsPluginStreamListenerPeer();
nsresult rv = listener->InitializeFullPage(aURI, aInstance);
if (NS_FAILED(rv)) {
return rv;
}
listener.forget(aStreamListener);
return NS_OK;
}
NS_IMETHODIMP nsPluginHost::Observe(nsISupports *aSubject,
const char *aTopic,
const PRUnichar *someData)

View File

@ -188,12 +188,6 @@ public:
nsObjectLoadingContent *aContent,
nsPluginInstanceOwner** aOwner);
nsresult InstantiateFullPagePluginInstance(const char *aMimeType,
nsIURI* aURI,
nsObjectLoadingContent *aContent,
nsPluginInstanceOwner **aOwner,
nsIStreamListener **aStreamListener);
// Does not accept NULL and should never fail.
nsPluginTag* TagForPlugin(nsNPAPIPlugin* aPlugin);
@ -203,10 +197,6 @@ public:
nsNPAPIPluginInstance* aInstance,
nsIStreamListener **aStreamListener);
nsresult NewFullPagePluginStreamListener(nsIURI* aURI,
nsNPAPIPluginInstance *aInstance,
nsIStreamListener **aStreamListener);
private:
nsresult
TrySetUpPluginInstance(const char *aMimeType, nsIURI *aURL, nsPluginInstanceOwner *aOwner);

View File

@ -377,26 +377,6 @@ nsresult nsPluginStreamListenerPeer::InitializeEmbedded(nsIURI *aURL,
return NS_OK;
}
// Called by NewFullPagePluginStream()
nsresult nsPluginStreamListenerPeer::InitializeFullPage(nsIURI* aURL, nsNPAPIPluginInstance *aInstance)
{
PLUGIN_LOG(PLUGIN_LOG_NORMAL,
("nsPluginStreamListenerPeer::InitializeFullPage instance=%p\n",aInstance));
NS_ASSERTION(mPluginInstance == nullptr, "nsPluginStreamListenerPeer::InitializeFullPage mPluginInstance != nullptr");
mPluginInstance = aInstance;
mURL = aURL;
mDataForwardToRequest = new nsHashtable(16, false);
if (!mDataForwardToRequest)
return NS_ERROR_FAILURE;
mPendingRequests = 1;
return NS_OK;
}
// SetupPluginCacheFile is called if we have to save the stream to disk.
// the most likely cause for this is either there is no disk cache available
// or the stream is coming from a https server.

View File

@ -64,25 +64,23 @@ public:
// Called by RequestRead
void
MakeByteRangeString(NPByteRange* aRangeList, nsACString &string, int32_t *numRequests);
bool UseExistingPluginCacheFile(nsPluginStreamListenerPeer* psi);
// Called by GetURL and PostURL (via NewStream)
nsresult Initialize(nsIURI *aURL,
nsNPAPIPluginInstance *aInstance,
nsNPAPIPluginStreamListener *aListener);
nsresult InitializeEmbedded(nsIURI *aURL,
nsNPAPIPluginInstance* aInstance);
nsresult InitializeFullPage(nsIURI* aURL, nsNPAPIPluginInstance *aInstance);
nsresult OnFileAvailable(nsIFile* aFile);
nsresult ServeStreamAsFile(nsIRequest *request, nsISupports *ctxt);
nsNPAPIPluginInstance *GetPluginInstance() { return mPluginInstance; }
nsresult RequestRead(NPByteRange* rangeList);
nsresult GetLength(uint32_t* result);
nsresult GetURL(const char** result);