michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "MediaDocument.h" michael@0: #include "nsIPluginDocument.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsIObjectFrame.h" michael@0: #include "nsNPAPIPluginInstance.h" michael@0: #include "nsIDocumentInlines.h" michael@0: #include "nsIDocShellTreeItem.h" michael@0: #include "nsNodeInfoManager.h" michael@0: #include "nsContentCreatorFunctions.h" michael@0: #include "nsContentPolicyUtils.h" michael@0: #include "nsIPropertyBag2.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "nsObjectLoadingContent.h" michael@0: #include "GeckoProfiler.h" michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: michael@0: class PluginDocument MOZ_FINAL : public MediaDocument michael@0: , public nsIPluginDocument michael@0: { michael@0: public: michael@0: PluginDocument(); michael@0: virtual ~PluginDocument(); michael@0: michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: NS_DECL_NSIPLUGINDOCUMENT michael@0: michael@0: virtual nsresult StartDocumentLoad(const char* aCommand, michael@0: nsIChannel* aChannel, michael@0: nsILoadGroup* aLoadGroup, michael@0: nsISupports* aContainer, michael@0: nsIStreamListener** aDocListener, michael@0: bool aReset = true, michael@0: nsIContentSink* aSink = nullptr); michael@0: michael@0: virtual void SetScriptGlobalObject(nsIScriptGlobalObject* aScriptGlobalObject); michael@0: virtual bool CanSavePresentation(nsIRequest *aNewRequest); michael@0: michael@0: const nsCString& GetType() const { return mMimeType; } michael@0: Element* GetPluginContent() { return mPluginContent; } michael@0: michael@0: void StartLayout() { MediaDocument::StartLayout(); } michael@0: michael@0: NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PluginDocument, MediaDocument) michael@0: protected: michael@0: nsresult CreateSyntheticPluginDocument(); michael@0: michael@0: nsCOMPtr mPluginContent; michael@0: nsRefPtr mStreamListener; michael@0: nsCString mMimeType; michael@0: }; michael@0: michael@0: class PluginStreamListener : public MediaDocumentStreamListener michael@0: { michael@0: public: michael@0: PluginStreamListener(PluginDocument* doc) michael@0: : MediaDocumentStreamListener(doc) michael@0: , mPluginDoc(doc) michael@0: {} michael@0: NS_IMETHOD OnStartRequest(nsIRequest* request, nsISupports *ctxt); michael@0: private: michael@0: nsRefPtr mPluginDoc; michael@0: }; michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: PluginStreamListener::OnStartRequest(nsIRequest* request, nsISupports *ctxt) michael@0: { michael@0: PROFILER_LABEL("PluginStreamListener", "OnStartRequest"); michael@0: michael@0: nsCOMPtr embed = mPluginDoc->GetPluginContent(); michael@0: nsCOMPtr objlc = do_QueryInterface(embed); michael@0: nsCOMPtr objListener = do_QueryInterface(objlc); michael@0: michael@0: if (!objListener) { michael@0: NS_NOTREACHED("PluginStreamListener without appropriate content node"); michael@0: return NS_BINDING_ABORTED; michael@0: } michael@0: michael@0: SetStreamListener(objListener); michael@0: michael@0: // Sets up the ObjectLoadingContent tag as if it is waiting for a michael@0: // channel, so it can proceed with a load normally once it gets OnStartRequest michael@0: nsresult rv = objlc->InitializeFromChannel(request); michael@0: if (NS_FAILED(rv)) { michael@0: NS_NOTREACHED("InitializeFromChannel failed"); michael@0: return rv; michael@0: } michael@0: michael@0: // Note that because we're now hooked up to a plugin listener, this will michael@0: // likely spawn a plugin, which may re-enter. michael@0: return MediaDocumentStreamListener::OnStartRequest(request, ctxt); michael@0: } michael@0: michael@0: // NOTE! nsDocument::operator new() zeroes out all members, so don't michael@0: // bother initializing members to 0. michael@0: michael@0: PluginDocument::PluginDocument() michael@0: {} michael@0: michael@0: PluginDocument::~PluginDocument() michael@0: {} michael@0: michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_INHERITED(PluginDocument, MediaDocument, michael@0: mPluginContent) michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(PluginDocument, MediaDocument) michael@0: NS_IMPL_RELEASE_INHERITED(PluginDocument, MediaDocument) michael@0: michael@0: NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(PluginDocument) michael@0: NS_INTERFACE_TABLE_INHERITED(PluginDocument, nsIPluginDocument) michael@0: NS_INTERFACE_TABLE_TAIL_INHERITING(MediaDocument) michael@0: michael@0: void michael@0: PluginDocument::SetScriptGlobalObject(nsIScriptGlobalObject* aScriptGlobalObject) michael@0: { michael@0: // Set the script global object on the superclass before doing michael@0: // anything that might require it.... michael@0: MediaDocument::SetScriptGlobalObject(aScriptGlobalObject); michael@0: michael@0: if (aScriptGlobalObject) { michael@0: if (!mPluginContent) { michael@0: // Create synthetic document michael@0: #ifdef DEBUG michael@0: nsresult rv = michael@0: #endif michael@0: CreateSyntheticPluginDocument(); michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), "failed to create synthetic document"); michael@0: } michael@0: BecomeInteractive(); michael@0: } else { michael@0: mStreamListener = nullptr; michael@0: } michael@0: } michael@0: michael@0: michael@0: bool michael@0: PluginDocument::CanSavePresentation(nsIRequest *aNewRequest) michael@0: { michael@0: // Full-page plugins cannot be cached, currently, because we don't have michael@0: // the stream listener data to feed to the plugin instance. michael@0: return false; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: PluginDocument::StartDocumentLoad(const char* aCommand, michael@0: nsIChannel* aChannel, michael@0: nsILoadGroup* aLoadGroup, michael@0: nsISupports* aContainer, michael@0: nsIStreamListener** aDocListener, michael@0: bool aReset, michael@0: nsIContentSink* aSink) michael@0: { michael@0: // do not allow message panes to host full-page plugins michael@0: // returning an error causes helper apps to take over michael@0: nsCOMPtr dsti (do_QueryInterface(aContainer)); michael@0: if (dsti) { michael@0: bool isMsgPane = false; michael@0: dsti->NameEquals(MOZ_UTF16("messagepane"), &isMsgPane); michael@0: if (isMsgPane) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: michael@0: nsresult rv = michael@0: MediaDocument::StartDocumentLoad(aCommand, aChannel, aLoadGroup, aContainer, michael@0: aDocListener, aReset, aSink); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: rv = aChannel->GetContentType(mMimeType); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: MediaDocument::UpdateTitleAndCharset(mMimeType); michael@0: michael@0: mStreamListener = new PluginStreamListener(this); michael@0: NS_ASSERTION(aDocListener, "null aDocListener"); michael@0: NS_ADDREF(*aDocListener = mStreamListener); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: PluginDocument::CreateSyntheticPluginDocument() michael@0: { michael@0: NS_ASSERTION(!GetShell() || !GetShell()->DidInitialize(), michael@0: "Creating synthetic plugin document content too late"); michael@0: michael@0: // make our generic document michael@0: nsresult rv = MediaDocument::CreateSyntheticDocument(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: // then attach our plugin michael@0: michael@0: Element* body = GetBodyElement(); michael@0: if (!body) { michael@0: NS_WARNING("no body on plugin document!"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // remove margins from body michael@0: NS_NAMED_LITERAL_STRING(zero, "0"); michael@0: body->SetAttr(kNameSpaceID_None, nsGkAtoms::marginwidth, zero, false); michael@0: body->SetAttr(kNameSpaceID_None, nsGkAtoms::marginheight, zero, false); michael@0: michael@0: michael@0: // make plugin content michael@0: nsCOMPtr nodeInfo; michael@0: nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::embed, nullptr, michael@0: kNameSpaceID_XHTML, michael@0: nsIDOMNode::ELEMENT_NODE); michael@0: rv = NS_NewHTMLElement(getter_AddRefs(mPluginContent), nodeInfo.forget(), michael@0: NOT_FROM_PARSER); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // make it a named element michael@0: mPluginContent->SetAttr(kNameSpaceID_None, nsGkAtoms::name, michael@0: NS_LITERAL_STRING("plugin"), false); michael@0: michael@0: // fill viewport and auto-resize michael@0: NS_NAMED_LITERAL_STRING(percent100, "100%"); michael@0: mPluginContent->SetAttr(kNameSpaceID_None, nsGkAtoms::width, percent100, michael@0: false); michael@0: mPluginContent->SetAttr(kNameSpaceID_None, nsGkAtoms::height, percent100, michael@0: false); michael@0: michael@0: // set URL michael@0: nsAutoCString src; michael@0: mDocumentURI->GetSpec(src); michael@0: mPluginContent->SetAttr(kNameSpaceID_None, nsGkAtoms::src, michael@0: NS_ConvertUTF8toUTF16(src), false); michael@0: michael@0: // set mime type michael@0: mPluginContent->SetAttr(kNameSpaceID_None, nsGkAtoms::type, michael@0: NS_ConvertUTF8toUTF16(mMimeType), false); michael@0: michael@0: // nsHTML(Shared)ObjectElement does not kick off a load on BindToTree if it is michael@0: // to a PluginDocument michael@0: body->AppendChildTo(mPluginContent, false); michael@0: michael@0: return NS_OK; michael@0: michael@0: michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: PluginDocument::Print() michael@0: { michael@0: NS_ENSURE_TRUE(mPluginContent, NS_ERROR_FAILURE); michael@0: michael@0: nsIObjectFrame* objectFrame = michael@0: do_QueryFrame(mPluginContent->GetPrimaryFrame()); michael@0: if (objectFrame) { michael@0: nsRefPtr pi; michael@0: objectFrame->GetPluginInstance(getter_AddRefs(pi)); michael@0: if (pi) { michael@0: NPPrint npprint; michael@0: npprint.mode = NP_FULL; michael@0: npprint.print.fullPrint.pluginPrinted = false; michael@0: npprint.print.fullPrint.printOne = false; michael@0: npprint.print.fullPrint.platformPrint = nullptr; michael@0: michael@0: pi->Print(&npprint); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: } // namespace dom michael@0: } // namespace mozilla michael@0: michael@0: nsresult michael@0: NS_NewPluginDocument(nsIDocument** aResult) michael@0: { michael@0: mozilla::dom::PluginDocument* doc = new mozilla::dom::PluginDocument(); michael@0: michael@0: NS_ADDREF(doc); michael@0: nsresult rv = doc->Init(); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: NS_RELEASE(doc); michael@0: } michael@0: michael@0: *aResult = doc; michael@0: michael@0: return rv; michael@0: }