/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Netscape Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Dan Rosen * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the NPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the NPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsCOMPtr.h" #include "nscore.h" #include "nsCRT.h" #include "nsIContentViewer.h" #include "nsIPluginHost.h" #include "nsIPluginInstance.h" #include "nsIStreamListener.h" #include "nsIURL.h" #include "nsIChannel.h" #include "nsNetUtil.h" #include "nsIComponentManager.h" #include "nsWidgetsCID.h" #include "nsILinkHandler.h" #include "nsIWebShell.h" #include "nsIContentViewerEdit.h" #include "nsIWebBrowserPrint.h" #include "nsIWidget.h" #include "nsIContent.h" #include "nsIDocument.h" #include "nsIInterfaceRequestor.h" #include "nsIInterfaceRequestorUtils.h" #include "nsIDocShellTreeItem.h" #include "nsIDocShellTreeOwner.h" #include "nsIWebBrowserChrome.h" #include "nsIDOMDocument.h" #include "nsPluginViewer.h" #include "nsGUIEvent.h" #include "nsIPluginViewer.h" #include "nsContentCID.h" #include "nsITimer.h" #include "nsITimerCallback.h" class nsIPrintSettings; class nsIDOMWindow; // Class IDs static NS_DEFINE_IID(kChildWindowCID, NS_CHILD_CID); static NS_DEFINE_IID(kIWidgetIID, NS_IWIDGET_IID); // Interface IDs static NS_DEFINE_IID(kIContentViewerIID, NS_ICONTENTVIEWER_IID); static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); static NS_DEFINE_IID(kCPluginManagerCID, NS_PLUGINMANAGER_CID); static NS_DEFINE_IID(kIDocumentIID, NS_IDOCUMENT_IID); static NS_DEFINE_IID(kHTMLDocumentCID, NS_HTMLDOCUMENT_CID); class PluginViewerImpl; class PluginListener : public nsIStreamListener { public: PluginListener(PluginViewerImpl* aViewer); virtual ~PluginListener(); // nsISupports NS_DECL_ISUPPORTS // nsIRequestObserver methods: NS_DECL_NSIREQUESTOBSERVER // nsIStreamListener methods: NS_DECL_NSISTREAMLISTENER PluginViewerImpl* mViewer; nsIStreamListener* mNextStream; }; class pluginInstanceOwner : public nsIPluginInstanceOwner, public nsITimerCallback { public: pluginInstanceOwner(); virtual ~pluginInstanceOwner(); NS_DECL_ISUPPORTS //nsIPluginInstanceOwner interface NS_IMETHOD SetInstance(nsIPluginInstance *aInstance); NS_IMETHOD GetInstance(nsIPluginInstance *&aInstance); NS_IMETHOD GetWindow(nsPluginWindow *&aWindow); NS_IMETHOD GetMode(nsPluginMode *aMode); NS_IMETHOD CreateWidget(void); NS_IMETHOD GetURL(const char *aURL, const char *aTarget, void *aPostData, PRUint32 aPostDataLen, void *aHeadersData, PRUint32 aHeadersDataLen, PRBool isFile = PR_FALSE); NS_IMETHOD ShowStatus(const char *aStatusMsg); NS_IMETHOD ShowStatus(const PRUnichar *aStatusMsg); NS_IMETHOD GetDocument(nsIDocument* *aDocument); NS_IMETHOD InvalidateRect(nsPluginRect *invalidRect); NS_IMETHOD InvalidateRegion(nsPluginRegion invalidRegion); NS_IMETHOD ForceRedraw(); NS_IMETHOD GetValue(nsPluginInstancePeerVariable variable, void *value); //nsIEventListener interface nsEventStatus ProcessEvent(const nsGUIEvent & anEvent); //locals NS_IMETHOD Init(PluginViewerImpl *aViewer, nsIWidget *aWindow); // nsITimerCallback interface NS_IMETHOD_(void) Notify(nsITimer *timer); void CancelTimer(); #ifdef XP_MAC void GUItoMacEvent(const nsGUIEvent& anEvent, EventRecord& aMacEvent); nsPluginPort* GetPluginPort(); void FixUpPluginWindow(); #endif private: nsPluginWindow mPluginWindow; nsIPluginInstance *mInstance; nsIWidget *mWindow; //we do not addref this... PluginViewerImpl *mViewer; //we do not addref this... nsCOMPtr mPluginTimer; }; static void GetWidgetPosAndClip(nsIWidget* aWidget,nscoord& aAbsX, nscoord& aAbsY, nsRect& aClipRect); class PluginViewerImpl : public nsIPluginViewer, public nsIContentViewer, public nsIContentViewerEdit, public nsIWebBrowserPrint { public: PluginViewerImpl(const char* aCommand); nsresult Init(nsIStreamListener** aDocListener); NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW // nsISupports NS_DECL_ISUPPORTS // nsIPluginViewer NS_IMETHOD StartLoad(nsIRequest* request, nsIStreamListener*& aResult); // nsIContentViewer NS_DECL_NSICONTENTVIEWER // nsIContentViewerEdit NS_DECL_NSICONTENTVIEWEREDIT // nsIWebBrowserPrint NS_DECL_NSIWEBBROWSERPRINT virtual ~PluginViewerImpl(); nsresult CreatePlugin(nsIRequest* request, nsIPluginHost* aHost, const nsRect& aBounds, nsIStreamListener*& aResult); nsresult MakeWindow(nsNativeWidget aParent, nsIDeviceContext* aDeviceContext, const nsRect& aBounds); void ForceRefresh(void); nsresult GetURI(nsIURI* *aURI); nsresult GetDocument(nsIDocument* *aDocument); nsIWidget* mWindow; nsIDocument* mDocument; nsCOMPtr mContainer; nsIChannel* mChannel; pluginInstanceOwner *mOwner; PRBool mEnableRendering; }; //---------------------------------------------------------------------- nsresult NS_NewPluginContentViewer(const char* aCommand, nsIStreamListener** aDocListener, nsIContentViewer** aDocViewer) { PluginViewerImpl* it = new PluginViewerImpl(aCommand); if (nsnull == it) { return NS_ERROR_OUT_OF_MEMORY; } nsresult rv = it->Init(aDocListener); if (NS_FAILED(rv)) { delete it; return rv; } return it->QueryInterface(kIContentViewerIID, (void**) aDocViewer); } // Note: operator new zeros our memory PluginViewerImpl::PluginViewerImpl(const char* aCommand) { NS_INIT_REFCNT(); mEnableRendering = PR_TRUE; } nsresult PluginViewerImpl::Init(nsIStreamListener** aDocListener) { nsIStreamListener* it = new PluginListener(this); if (it == nsnull) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(it); *aDocListener = it; return NS_OK; } // ISupports implementation... NS_IMPL_ADDREF(PluginViewerImpl) NS_IMPL_RELEASE(PluginViewerImpl) NS_IMPL_QUERY_INTERFACE4(PluginViewerImpl, nsIPluginViewer, nsIContentViewer, nsIContentViewerEdit, nsIWebBrowserPrint) PluginViewerImpl::~PluginViewerImpl() { #ifdef XP_MAC if (mOwner) mOwner->CancelTimer(); #endif if(mOwner) { nsIPluginInstance * inst; if(NS_SUCCEEDED(mOwner->GetInstance(inst)) && inst) { nsCOMPtr host = do_GetService(kCPluginManagerCID); if(host) host->StopPluginInstance(inst); NS_RELEASE(inst); } } NS_IF_RELEASE(mOwner); if (nsnull != mWindow) { mWindow->Destroy(); NS_RELEASE(mWindow); } NS_IF_RELEASE(mDocument); NS_IF_RELEASE(mChannel); } NS_IMETHODIMP PluginViewerImpl::SetContainer(nsISupports* aContainer) { mContainer = aContainer; return NS_OK; } NS_IMETHODIMP PluginViewerImpl::GetContainer(nsISupports** aResult) { NS_ENSURE_ARG_POINTER(aResult); *aResult = mContainer; NS_IF_ADDREF(*aResult); return NS_OK; } NS_IMETHODIMP PluginViewerImpl::Init(nsIWidget* aParentWidget, nsIDeviceContext* aDeviceContext, const nsRect& aBounds) { nsresult rv = MakeWindow(aParentWidget->GetNativeData(NS_NATIVE_WIDGET), aDeviceContext, aBounds); if (NS_OK == rv) { mOwner = new pluginInstanceOwner(); if (nsnull != mOwner) { NS_ADDREF(mOwner); rv = mOwner->Init(this, mWindow); } } return rv; } NS_IMETHODIMP PluginViewerImpl::StartLoad(nsIRequest* request, nsIStreamListener*& aResult) { nsCOMPtr channel = do_QueryInterface(request); if (!channel) return NS_ERROR_FAILURE; NS_IF_RELEASE(mChannel); mChannel = channel; NS_ADDREF(mChannel); #ifdef DEBUG char* contentType; channel->GetContentType(&contentType); printf("PluginViewerImpl::StartLoad: content-type=%s\n", contentType); nsCRT::free(contentType); #endif aResult = nsnull; // Only instantiate the plugin if our container can host it nsCOMPtr host; host = do_GetService(kCPluginManagerCID); nsresult rv = NS_ERROR_FAILURE; if(host) { // create a document so that we can pass something back to plugin // instance if it wants one nsCOMPtr doc(do_CreateInstance(kHTMLDocumentCID)); if (doc) { mDocument = doc; NS_ADDREF(mDocument); // released in ~nsPluginViewer // set the document's URL // so it can be fetched later for resolving relative URLs nsCOMPtr uri; GetURI(getter_AddRefs(uri)); if (uri) mDocument->SetDocumentURL(uri); } nsRect r; mWindow->GetClientBounds(r); rv = CreatePlugin(request, host, nsRect(0, 0, r.width, r.height), aResult); } return rv; } nsresult PluginViewerImpl::CreatePlugin(nsIRequest* request, nsIPluginHost* aHost, const nsRect& aBounds, nsIStreamListener*& aResult) { nsresult rv = NS_OK; if (nsnull != mOwner) { nsPluginWindow *win; mOwner->GetWindow(win); win->x = aBounds.x; win->y = aBounds.y; win->width = aBounds.width; win->height = aBounds.height; win->clipRect.top = aBounds.y; win->clipRect.left = aBounds.x; win->clipRect.bottom = aBounds.YMost(); win->clipRect.right = aBounds.XMost(); #ifdef XP_UNIX win->ws_info = nsnull; //XXX need to figure out what this is. MMP #endif nsIURI* uri; rv = mChannel->GetURI(&uri); if (NS_FAILED(rv)) return rv; nsCAutoString spec; rv = uri->GetSpec(spec); NS_RELEASE(uri); if (NS_FAILED(rv)) return rv; NS_ConvertUTF8toUCS2 str(spec); char* ct; nsCOMPtr channel = do_QueryInterface(request); channel->GetContentType(&ct); if (NS_FAILED(rv)) return rv; rv = aHost->InstantiateFullPagePlugin(ct, str, aResult, mOwner); delete[] ct; } return rv; } NS_IMETHODIMP PluginViewerImpl::Stop(void) { // XXX write this return NS_OK; } /* * This method is called by the Document Loader once a document has * been created for a particular data stream... The content viewer * must cache this document for later use when Init(...) is called. */ NS_IMETHODIMP PluginViewerImpl::LoadStart(nsISupports *aDoc) { #ifdef NS_DEBUG printf("PluginViewerImpl::LoadStart\n"); #endif return aDoc->QueryInterface(kIDocumentIID, (void**)&mDocument); } NS_IMETHODIMP PluginViewerImpl::LoadComplete(nsresult aStatus) { return NS_ERROR_FAILURE; } NS_IMETHODIMP PluginViewerImpl::Unload(void) { return NS_OK; } NS_IMETHODIMP PluginViewerImpl::Close(void) { return NS_OK; } NS_IMETHODIMP PluginViewerImpl::Destroy(void) { // XXX ripped off from nsObjectFrame::Destroy() // we need to finish with the plugin before native window is destroyed // doing this in the destructor is too late. if(mOwner != nsnull) { nsIPluginInstance *inst; if(NS_OK == mOwner->GetInstance(inst)) { PRBool doCache = PR_TRUE; PRBool doCallSetWindowAfterDestroy = PR_FALSE; // first, determine if the plugin wants to be cached inst->GetValue(nsPluginInstanceVariable_DoCacheBool, (void *) &doCache); if (!doCache) { // then determine if the plugin wants Destroy to be called after // Set Window. This is for bug 50547. inst->GetValue(nsPluginInstanceVariable_CallSetWindowAfterDestroyBool, (void *) &doCallSetWindowAfterDestroy); !doCallSetWindowAfterDestroy ? inst->SetWindow(nsnull) : 0; inst->Stop(); inst->Destroy(); doCallSetWindowAfterDestroy ? inst->SetWindow(nsnull) : 0; } else { inst->SetWindow(nsnull); inst->Stop(); } NS_RELEASE(inst); } } return NS_OK; } NS_IMETHODIMP PluginViewerImpl::GetDOMDocument(nsIDOMDocument **aResult) { return (mDocument) ? CallQueryInterface(mDocument, aResult) : NS_ERROR_FAILURE; } NS_IMETHODIMP PluginViewerImpl::SetDOMDocument(nsIDOMDocument *aDocument) { return NS_ERROR_FAILURE; } nsEventStatus PR_CALLBACK HandlePluginEvent(nsGUIEvent *aEvent) { if (aEvent == nsnull || aEvent->widget == nsnull) //null pointer check return nsEventStatus_eIgnore; #ifdef XP_WIN // on Windows, the mouse click is converted to an NS_PLUGIN_ACTIVATE if( aEvent->message == NS_PLUGIN_ACTIVATE) (nsIWidget*)(aEvent->widget)->SetFocus(); // send focus to child window #else // the Mac, and presumably others, send NS_MOUSE_ACTIVATE if (aEvent->message == NS_MOUSE_ACTIVATE) { (nsIWidget*)(aEvent->widget)->SetFocus(); // send focus to child window #ifdef XP_MAC // furthermore on the Mac nsMacEventHandler sends the NS_PLUGIN_ACTIVATE // followed by the mouse down event, so we need to handle this } else { // on Mac, we store a pointer to this class as native data in the widget void *clientData; (nsIWidget*)(aEvent->widget)->GetClientData(clientData); PluginViewerImpl * pluginViewer = (PluginViewerImpl *)clientData; if (pluginViewer != nsnull && pluginViewer->mOwner != nsnull) return pluginViewer->mOwner->ProcessEvent(*aEvent); #endif // XP_MAC } #endif // else XP_WIN return nsEventStatus_eIgnore; } nsresult PluginViewerImpl::MakeWindow(nsNativeWidget aParent, nsIDeviceContext* aDeviceContext, const nsRect& aBounds) { nsresult rv = nsComponentManager::CreateInstance(kChildWindowCID, nsnull, kIWidgetIID, (void**)&mWindow); if (NS_OK != rv) { return rv; } mWindow->Create(aParent, aBounds,HandlePluginEvent, aDeviceContext); mWindow->SetClientData(this); Show(); return rv; } NS_IMETHODIMP PluginViewerImpl::GetBounds(nsRect& aResult) { NS_PRECONDITION(nsnull != mWindow, "null window"); if (nsnull != mWindow) { mWindow->GetBounds(aResult); } else { aResult.SetRect(0, 0, 0, 0); } return NS_OK; } NS_IMETHODIMP PluginViewerImpl::GetPreviousViewer(nsIContentViewer** aViewer) { *aViewer = nsnull; return NS_OK; } NS_IMETHODIMP PluginViewerImpl::SetPreviousViewer(nsIContentViewer* aViewer) { if (aViewer) aViewer->Destroy(); return NS_OK; } NS_IMETHODIMP PluginViewerImpl::SetBounds(const nsRect& aBounds) { NS_PRECONDITION(nsnull != mWindow, "null window"); if (nsnull != mWindow) { // Don't have the widget repaint. Layout will generate repaint requests // during reflow nsIPluginInstance *inst; mWindow->Resize(aBounds.x, aBounds.y, aBounds.width, aBounds.height, PR_FALSE); if ((nsnull != mOwner) && (NS_OK == mOwner->GetInstance(inst)) && (nsnull != inst)) { nsPluginWindow *win; if (NS_OK == mOwner->GetWindow(win)) { win->x = aBounds.x; win->y = aBounds.y; win->width = aBounds.width; win->height = aBounds.height; win->clipRect.top = aBounds.y; win->clipRect.left = aBounds.x; win->clipRect.bottom = aBounds.YMost(); win->clipRect.right = aBounds.XMost(); inst->SetWindow(win); } NS_RELEASE(inst); } } return NS_OK; } NS_IMETHODIMP PluginViewerImpl::Move(PRInt32 aX, PRInt32 aY) { NS_PRECONDITION(nsnull != mWindow, "null window"); if (nsnull != mWindow) { nsIPluginInstance *inst; mWindow->Move(aX, aY); if ((nsnull != mOwner) && (NS_OK == mOwner->GetInstance(inst)) && (nsnull != inst)) { nsPluginWindow *win; if (NS_OK == mOwner->GetWindow(win)) { win->x = aX; win->y = aY; win->clipRect.bottom = (win->clipRect.bottom - win->clipRect.top) + aY; win->clipRect.right = (win->clipRect.right - win->clipRect.left) + aX; win->clipRect.top = aY; win->clipRect.left = aX; inst->SetWindow(win); } NS_RELEASE(inst); } } return NS_OK; } NS_IMETHODIMP PluginViewerImpl::Show() { NS_PRECONDITION(nsnull != mWindow, "null window"); if (nsnull != mWindow) { mWindow->Show(PR_TRUE); } // XXX should we call SetWindow here? return NS_OK; } NS_IMETHODIMP PluginViewerImpl::Hide() { NS_PRECONDITION(nsnull != mWindow, "null window"); if (nsnull != mWindow) { mWindow->Show(PR_FALSE); } // should we call SetWindow(nsnull) here? return NS_OK; } NS_IMETHODIMP PluginViewerImpl::SetEnableRendering(PRBool aOn) { mEnableRendering = aOn; return NS_OK; } NS_IMETHODIMP PluginViewerImpl::GetEnableRendering(PRBool* aResult) { NS_PRECONDITION(nsnull != aResult, "null OUT ptr"); if (aResult) { *aResult = mEnableRendering; } return NS_OK; } void PluginViewerImpl::ForceRefresh() { mWindow->Invalidate(PR_TRUE); } nsresult PluginViewerImpl::GetURI(nsIURI* *aURI) { return mChannel->GetURI(aURI); } nsresult PluginViewerImpl::GetDocument(nsIDocument* *aDocument) { NS_IF_ADDREF(mDocument); *aDocument = mDocument; return NS_OK; } NS_IMETHODIMP pluginInstanceOwner::InvalidateRect(nsPluginRect *invalidRect) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP pluginInstanceOwner::InvalidateRegion(nsPluginRegion invalidRegion) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP pluginInstanceOwner::ForceRedraw() { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP pluginInstanceOwner::GetValue(nsPluginInstancePeerVariable variable, void *value) { return NS_ERROR_NOT_IMPLEMENTED; } /* ======================================================================================== * nsIContentViewerEdit * ======================================================================================== */ NS_IMETHODIMP PluginViewerImpl::Search() { NS_ASSERTION(0, "NOT IMPLEMENTED"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP PluginViewerImpl::GetSearchable(PRBool *aSearchable) { NS_ASSERTION(0, "NOT IMPLEMENTED"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP PluginViewerImpl::ClearSelection() { NS_ASSERTION(0, "NOT IMPLEMENTED"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP PluginViewerImpl::SelectAll() { NS_ASSERTION(0, "NOT IMPLEMENTED"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP PluginViewerImpl::CopySelection() { NS_ASSERTION(0, "NOT IMPLEMENTED"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP PluginViewerImpl::GetCopyable(PRBool *aCopyable) { NS_ASSERTION(0, "NOT IMPLEMENTED"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP PluginViewerImpl::CutSelection() { NS_ASSERTION(0, "NOT IMPLEMENTED"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP PluginViewerImpl::GetCutable(PRBool *aCutable) { NS_ASSERTION(0, "NOT IMPLEMENTED"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP PluginViewerImpl::Paste() { NS_ASSERTION(0, "NOT IMPLEMENTED"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP PluginViewerImpl::GetPasteable(PRBool *aPasteable) { NS_ASSERTION(0, "NOT IMPLEMENTED"); return NS_ERROR_NOT_IMPLEMENTED; } /* ======================================================================================== * nsIWebBrowserPrint * ======================================================================================== */ NS_IMETHODIMP PluginViewerImpl::Print(nsIPrintSettings* aPrintSettings, nsIWebProgressListener* aWebProgressListener) { nsPluginPrint npprint; npprint.mode = nsPluginMode_Full; npprint.print.fullPrint.pluginPrinted = PR_FALSE; npprint.print.fullPrint.printOne = PR_FALSE; npprint.print.fullPrint.platformPrint = nsnull; NS_ENSURE_TRUE(mOwner,NS_ERROR_FAILURE); nsCOMPtr pi; mOwner->GetInstance(*getter_AddRefs(pi)); NS_ENSURE_TRUE(pi,NS_ERROR_FAILURE); return pi->Print(&npprint); } NS_IMETHODIMP PluginViewerImpl::GetNewPrintSettings(nsIPrintSettings * *aNewPrintSettings) { return NS_ERROR_NOT_IMPLEMENTED; } /* readonly attribute wstring defaultPrinterName; */ NS_IMETHODIMP PluginViewerImpl::GetDefaultPrinterName(PRUnichar * *aDefaultPrinterName) { return NS_ERROR_NOT_IMPLEMENTED; } /* void initPrintSettingsFromPrinter (in wstring aPrinterName, in nsIPrintSettings aPrintSettings); */ NS_IMETHODIMP PluginViewerImpl::InitPrintSettingsFromPrinter(const PRUnichar *aPrinterName, nsIPrintSettings *aPrintSettings) { return NS_ERROR_NOT_IMPLEMENTED; } /* readonly attribute nsIPrintSettings globalPrintSettings; */ NS_IMETHODIMP PluginViewerImpl::GetGlobalPrintSettings(nsIPrintSettings * *aGlobalPrintSettings) { return NS_ERROR_NOT_IMPLEMENTED; } /* attribute nsIPrintSettings globalPrintSettingsValues; */ NS_IMETHODIMP PluginViewerImpl::GetGlobalPrintSettingsValues(nsIPrintSettings * *aGlobalPrintSettingsValues) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP PluginViewerImpl::SetGlobalPrintSettingsValues(nsIPrintSettings * aGlobalPrintSettingsValues) { return NS_ERROR_NOT_IMPLEMENTED; } /* void printPreview (in nsIPrintSettings aThePrintSettings); */ NS_IMETHODIMP PluginViewerImpl::PrintPreview(nsIPrintSettings *aThePrintSettings) { return NS_ERROR_NOT_IMPLEMENTED; } /* void printPreviewNavigate (in short aNavType, in long aPageNum); */ NS_IMETHODIMP PluginViewerImpl::PrintPreviewNavigate(PRInt16 aNavType, PRInt32 aPageNum) { return NS_ERROR_NOT_IMPLEMENTED; } /* readonly attribute boolean doingPrintPreview; */ NS_IMETHODIMP PluginViewerImpl::GetDoingPrintPreview(PRBool *aDoingPrintPreview) { return NS_ERROR_NOT_IMPLEMENTED; } /* readonly attribute nsIPrintSettings currentPrintSettings; */ NS_IMETHODIMP PluginViewerImpl::GetCurrentPrintSettings(nsIPrintSettings * *aCurrentPrintSettings) { return NS_ERROR_NOT_IMPLEMENTED; } /* void cancel (); */ NS_IMETHODIMP PluginViewerImpl::Cancel() { return NS_ERROR_NOT_IMPLEMENTED; } /* void enumerateDocumentNames (out PRUint32 aCount, [array, size_is (aCount), retval] out wstring aResult); */ NS_IMETHODIMP PluginViewerImpl::EnumerateDocumentNames(PRUint32 *aCount, PRUnichar ***aResult) { return NS_ERROR_NOT_IMPLEMENTED; } /* readonly attribute boolean isFramesetDocument; */ NS_IMETHODIMP PluginViewerImpl::GetIsFramesetDocument(PRBool *aIsFramesetDocument) { return NS_ERROR_NOT_IMPLEMENTED; } /* readonly attribute long printPreviewNumPages; */ NS_IMETHODIMP PluginViewerImpl::GetPrintPreviewNumPages(PRInt32 *aPrintPreviewNumPages) { return NS_ERROR_NOT_IMPLEMENTED; } /* void exitPrintPreview (); */ NS_IMETHODIMP PluginViewerImpl::ExitPrintPreview() { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP PluginViewerImpl::GetInLink(PRBool* aInLink) { NS_ENSURE_ARG_POINTER(aInLink); *aInLink = PR_FALSE; return NS_OK; } NS_IMETHODIMP PluginViewerImpl::GetInImage(PRBool* aInImage) { NS_ENSURE_ARG_POINTER(aInImage); *aInImage = PR_FALSE; return NS_OK; } NS_IMETHODIMP PluginViewerImpl::CopyLinkLocation() { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP PluginViewerImpl::CopyImageLocation() { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP PluginViewerImpl::CopyImageContents() { return NS_ERROR_NOT_IMPLEMENTED; } //---------------------------------------------------------------------- PluginListener::PluginListener(PluginViewerImpl* aViewer) { NS_INIT_REFCNT(); mViewer = aViewer; NS_ADDREF(aViewer); } PluginListener::~PluginListener() { NS_RELEASE(mViewer); NS_IF_RELEASE(mNextStream); } NS_IMPL_ISUPPORTS1(PluginListener, nsIStreamListener) NS_IMETHODIMP PluginListener::OnStartRequest(nsIRequest *request, nsISupports *ctxt) { nsresult rv; char* contentType = nsnull; nsCOMPtr channel = do_QueryInterface(request); rv = channel->GetContentType(&contentType); if (NS_FAILED(rv)) { return rv; } rv = mViewer->StartLoad(request, mNextStream); if (NS_FAILED(rv)) { return rv; } if (nsnull == mNextStream) return NS_ERROR_FAILURE; return mNextStream->OnStartRequest(request, ctxt); } NS_IMETHODIMP PluginListener::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult status) { if (nsnull == mNextStream) { return NS_ERROR_FAILURE; } return mNextStream->OnStopRequest(request, ctxt, status); } NS_IMETHODIMP PluginListener::OnDataAvailable(nsIRequest *request, nsISupports *ctxt, nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count) { if (nsnull == mNextStream) { return NS_ERROR_FAILURE; } return mNextStream->OnDataAvailable(request, ctxt, inStr, sourceOffset, count); } //---------------------------------------------------------------------- pluginInstanceOwner :: pluginInstanceOwner() { NS_INIT_REFCNT(); memset(&mPluginWindow, 0, sizeof(mPluginWindow)); mInstance = nsnull; mWindow = nsnull; mViewer = nsnull; } pluginInstanceOwner :: ~pluginInstanceOwner() { // shut off the timer. if (mPluginTimer != nsnull) { CancelTimer(); } NS_IF_RELEASE(mInstance); mWindow = nsnull; mViewer = nsnull; } NS_IMPL_ISUPPORTS2(pluginInstanceOwner, nsIPluginInstanceOwner,nsITimerCallback); NS_IMETHODIMP pluginInstanceOwner :: SetInstance(nsIPluginInstance *aInstance) { NS_IF_RELEASE(mInstance); mInstance = aInstance; NS_IF_ADDREF(mInstance); return NS_OK; } NS_IMETHODIMP pluginInstanceOwner :: GetInstance(nsIPluginInstance *&aInstance) { if(!mInstance) return NS_ERROR_FAILURE; NS_ADDREF(mInstance); aInstance = mInstance; return NS_OK; } NS_IMETHODIMP pluginInstanceOwner :: GetWindow(nsPluginWindow *&aWindow) { aWindow = &mPluginWindow; return NS_OK; } NS_IMETHODIMP pluginInstanceOwner :: GetMode(nsPluginMode *aMode) { *aMode = nsPluginMode_Full; return NS_OK; } NS_IMETHODIMP pluginInstanceOwner :: CreateWidget(void) { PRBool windowless; nsresult rv = NS_OK; if (nsnull != mInstance) { #if defined(XP_MAC) // start a periodic timer to provide null events to the plugin instance. mPluginTimer = do_CreateInstance("@mozilla.org/timer;1", &rv); if (rv == NS_OK) rv = mPluginTimer->Init(this, 1020 / 60, NS_PRIORITY_NORMAL, NS_TYPE_REPEATING_SLACK); #endif mInstance->GetValue(nsPluginInstanceVariable_WindowlessBool, (void *)&windowless); if (PR_TRUE == windowless) { mPluginWindow.window = nsnull; //XXX this needs to be a HDC mPluginWindow.type = nsPluginWindowType_Drawable; } else if (nsnull != mWindow) { mPluginWindow.window = (nsPluginPort *)mWindow->GetNativeData(NS_NATIVE_PLUGIN_PORT); mPluginWindow.type = nsPluginWindowType_Window; } else return NS_ERROR_FAILURE; } else return NS_ERROR_FAILURE; #if defined(XP_MAC) FixUpPluginWindow(); #endif return rv; } NS_IMETHODIMP pluginInstanceOwner::GetURL(const char *aURL, const char *aTarget, void *aPostData, PRUint32 aPostDataLen, void *aHeadersData, PRUint32 aHeadersDataLen, PRBool isFile) { NS_ENSURE_TRUE(mViewer,NS_ERROR_NULL_POINTER); // the container of the pres context will give us the link handler nsCOMPtr container; nsresult rv = mViewer->GetContainer(getter_AddRefs(container)); NS_ENSURE_TRUE(container,NS_ERROR_FAILURE); nsCOMPtr lh = do_QueryInterface(container); NS_ENSURE_TRUE(lh, NS_ERROR_FAILURE); nsCOMPtr uri; rv = mViewer->GetURI(getter_AddRefs(uri)); NS_ENSURE_TRUE(NS_SUCCEEDED(rv),NS_ERROR_FAILURE); char* absURIStr; NS_MakeAbsoluteURI(&absURIStr, aURL, uri); nsAutoString fullurl; fullurl.AssignWithConversion(absURIStr); nsCRT::free(absURIStr); NS_ENSURE_TRUE(NS_SUCCEEDED(rv),NS_ERROR_FAILURE); nsCOMPtr postDataStream; nsCOMPtr headersDataStream; // deal with post data, either in a file or raw data, and any headers if (aPostData) { rv = NS_NewPluginPostDataStream(getter_AddRefs(postDataStream), (const char *)aPostData, aPostDataLen, isFile); NS_ASSERTION(NS_SUCCEEDED(rv),"failed in creating plugin post data stream"); if (NS_FAILED(rv)) return rv; if (aHeadersData) { rv = NS_NewPluginPostDataStream(getter_AddRefs(headersDataStream), (const char *) aHeadersData, aHeadersDataLen, PR_FALSE, PR_TRUE); // last arg says we are headers NS_ASSERTION(NS_SUCCEEDED(rv),"failed in creating plugin header data stream"); if (NS_FAILED(rv)) return rv; } } nsAutoString unitarget; unitarget.AssignWithConversion(aTarget); rv = lh->OnLinkClick(nsnull, eLinkVerb_Replace, fullurl.get(), unitarget.get(), postDataStream, headersDataStream); return rv; } NS_IMETHODIMP pluginInstanceOwner :: ShowStatus(const char *aStatusMsg) { nsresult rv = NS_ERROR_FAILURE; rv = this->ShowStatus(NS_ConvertUTF8toUCS2(aStatusMsg).get()); return rv; } NS_IMETHODIMP pluginInstanceOwner::ShowStatus(const PRUnichar *aStatusMsg) { nsresult rv = NS_ERROR_FAILURE; if (!mViewer) { return rv; } nsCOMPtr cont; nsCOMPtr treeOwner; rv = mViewer->GetContainer(getter_AddRefs(cont)); if (NS_FAILED(rv) || !cont) { return rv; } nsCOMPtr docShellItem(do_QueryInterface(cont, &rv)); if (NS_FAILED(rv) || !docShellItem) { return rv; } rv = docShellItem->GetTreeOwner(getter_AddRefs(treeOwner)); if (NS_FAILED(rv) || !treeOwner) { return rv; } nsCOMPtr browserChrome(do_GetInterface(treeOwner, &rv)); if (NS_FAILED(rv) || !browserChrome) { return rv; } rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_SCRIPT, aStatusMsg); return rv; } NS_IMETHODIMP pluginInstanceOwner :: GetDocument(nsIDocument* *aDocument) { return mViewer->GetDocument(aDocument); } NS_IMETHODIMP pluginInstanceOwner :: Init(PluginViewerImpl *aViewer, nsIWidget *aWindow) { //do not addref mWindow = aWindow; mViewer = aViewer; return NS_OK; } // Here's where we forward events to plugins. #ifdef XP_MAC #if TARGET_CARBON inline Boolean OSEventAvail(EventMask mask, EventRecord* event) { return EventAvail(mask, event); } #endif void pluginInstanceOwner::GUItoMacEvent(const nsGUIEvent& anEvent, EventRecord& aMacEvent) { ::OSEventAvail(0, &aMacEvent); switch (anEvent.message) { case NS_GOTFOCUS: case NS_FOCUS_EVENT_START: aMacEvent.what = nsPluginEventType_GetFocusEvent; break; case NS_LOSTFOCUS: case NS_MOUSE_EXIT: aMacEvent.what = nsPluginEventType_LoseFocusEvent; break; case NS_MOUSE_MOVE: case NS_MOUSE_ENTER: mWindow->SetFocus(); aMacEvent.what = nsPluginEventType_AdjustCursorEvent; break; case NS_PAINT: aMacEvent.what = updateEvt; break; case NS_KEY_DOWN: case NS_KEY_PRESS: break; default: aMacEvent.what = nullEvent; break; } } #endif nsEventStatus pluginInstanceOwner::ProcessEvent(const nsGUIEvent& anEvent) { nsEventStatus rv = nsEventStatus_eIgnore; if (!mInstance || !mWindow) // if the instance or the window is null, we shouldn't be here return rv; #ifdef XP_MAC //if (mWidget != NULL) { // check for null mWidget EventRecord* event = (EventRecord*)anEvent.nativeMsg; if (event == NULL || event->what == nullEvent || anEvent.message == NS_KEY_PRESS || anEvent.message == NS_CONTEXTMENU_MESSAGE_START) { EventRecord macEvent; GUItoMacEvent(anEvent, macEvent); event = &macEvent; if (event->what == updateEvt) { nsPluginPort* pluginPort = GetPluginPort(); // Add in child windows absolute position to get make the dirty rect // relative to the top-level window. nscoord absWidgetX = 0; nscoord absWidgetY = 0; nsRect widgetClip(0,0,0,0); GetWidgetPosAndClip(mWindow,absWidgetX,absWidgetY,widgetClip); //mViewer->GetBounds(widgetClip); //absWidgetX = widgetClip.x; //absWidgetY = widgetClip.y; // set the port mPluginWindow.x = absWidgetX; mPluginWindow.y = absWidgetY; // fix up the clipping region mPluginWindow.clipRect.top = widgetClip.y; mPluginWindow.clipRect.left = widgetClip.x; mPluginWindow.clipRect.bottom = mPluginWindow.clipRect.top + widgetClip.height; mPluginWindow.clipRect.right = mPluginWindow.clipRect.left + widgetClip.width; EventRecord updateEvent; ::OSEventAvail(0, &updateEvent); updateEvent.what = updateEvt; updateEvent.message = UInt32(pluginPort->port); nsPluginEvent pluginEvent = { &updateEvent, nsPluginPlatformWindowRef(pluginPort->port) }; PRBool eventHandled = PR_FALSE; mInstance->HandleEvent(&pluginEvent, &eventHandled); } return nsEventStatus_eConsumeNoDefault; } //nsPluginPort* port = (nsPluginPort*)mWidget->GetNativeData(NS_NATIVE_PLUGIN_PORT); nsPluginPort* port = (nsPluginPort*)mWindow->GetNativeData(NS_NATIVE_PLUGIN_PORT); nsPluginEvent pluginEvent = { event, nsPluginPlatformWindowRef(port->port) }; PRBool eventHandled = PR_FALSE; mInstance->HandleEvent(&pluginEvent, &eventHandled); if (eventHandled && anEvent.message != NS_MOUSE_LEFT_BUTTON_DOWN) rv = nsEventStatus_eConsumeNoDefault; // } #endif //~~~ #ifdef XP_WIN nsPluginEvent * pPluginEvent = (nsPluginEvent *)anEvent.nativeMsg; PRBool eventHandled = PR_FALSE; mInstance->HandleEvent(pPluginEvent, &eventHandled); if (eventHandled) rv = nsEventStatus_eConsumeNoDefault; #endif return rv; } // Here's how we give idle time to plugins. NS_IMETHODIMP_(void) pluginInstanceOwner::Notify(nsITimer* /* timer */) { #ifdef XP_MAC // validate the plugin clipping information by syncing the plugin window info to // reflect the current widget location. This makes sure that everything is updated // correctly in the event of scrolling in the window. FixUpPluginWindow(); if (mInstance != NULL) { EventRecord idleEvent; ::OSEventAvail(0, &idleEvent); idleEvent.what = nullEvent; nsPluginPort* pluginPort = GetPluginPort(); nsPluginEvent pluginEvent = { &idleEvent, nsPluginPlatformWindowRef(pluginPort->port) }; PRBool eventHandled = PR_FALSE; mInstance->HandleEvent(&pluginEvent, &eventHandled); } #ifndef REPEATING_TIMERS // reprime the timer? currently have to create a new timer for each call, which is // kind of wasteful. need to get periodic timers working on all platforms. nsresult rv; mPluginTimer = do_CreateInstance("@mozilla.org/timer;1", &rv); if (NS_SUCCEEDED(rv)) mPluginTimer->Init(this, 1020 / 60); #endif // REPEATING_TIMERS #endif // XP_MAC } void pluginInstanceOwner::CancelTimer() { if (mPluginTimer) { mPluginTimer->Cancel(); mPluginTimer = nsnull; } } #ifdef XP_MAC nsPluginPort* pluginInstanceOwner::GetPluginPort() { nsPluginPort* result = NULL; if (mWindow != NULL) result = (nsPluginPort*) mWindow->GetNativeData(NS_NATIVE_PLUGIN_PORT); return result; } // calculate the absolute position and clip for a widget // and use other windows in calculating the clip static void GetWidgetPosAndClip(nsIWidget* aWidget,nscoord& aAbsX, nscoord& aAbsY, nsRect& aClipRect) { aWidget->GetBounds(aClipRect); aAbsX = aClipRect.x; aAbsY = aClipRect.y; nscoord ancestorX = -aClipRect.x, ancestorY = -aClipRect.y; // Calculate clipping relative to the widget passed in aClipRect.x = 0; aClipRect.y = 0; // Gather up the absolute position of the widget // + clip window nsCOMPtr widget = getter_AddRefs(aWidget->GetParent()); while (widget != nsnull) { nsRect wrect; widget->GetClientBounds(wrect); nscoord wx, wy; wx = wrect.x; wy = wrect.y; wrect.x = ancestorX; wrect.y = ancestorY; aClipRect.IntersectRect(aClipRect, wrect); aAbsX += wx; aAbsY += wy; widget = getter_AddRefs(widget->GetParent()); if (widget == nsnull) { // Don't include the top-level windows offset // printf("Top level window offset %d %d\n", wx, wy); aAbsX -= wx; aAbsY -= wy; } ancestorX -=wx; ancestorY -=wy; } aClipRect.x += aAbsX; aClipRect.y += aAbsY; //printf("--------------\n"); //printf("Widget clip X %d Y %d rect %d %d %d %d\n", aAbsX, aAbsY, aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height ); //printf("--------------\n"); } void pluginInstanceOwner::FixUpPluginWindow() { if (mWindow) { nscoord absWidgetX = 0; nscoord absWidgetY = 0; nsRect widgetClip(0,0,0,0); GetWidgetPosAndClip(mWindow,absWidgetX,absWidgetY,widgetClip); // set the port coordinates mPluginWindow.x = absWidgetX; mPluginWindow.y = absWidgetY; // fix up the clipping region mPluginWindow.clipRect.top = widgetClip.y; mPluginWindow.clipRect.left = widgetClip.x; mPluginWindow.clipRect.bottom = mPluginWindow.clipRect.top + widgetClip.height; mPluginWindow.clipRect.right = mPluginWindow.clipRect.left + widgetClip.width; } } #endif