/* -*- Mode: C++; tab-width: 4; 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.org 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): * Tim Copperfield * Roland Mainz * * 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 "prlog.h" #include "prmem.h" #include "nscore.h" #include "ns4xPluginInstance.h" #include "ns4xPluginStreamListener.h" #include "nsPluginHostImpl.h" #include "nsPluginSafety.h" #include "nsIPref.h" // needed for NS_TRY_SAFE_CALL_* #include "nsPluginLogging.h" #if defined(MOZ_WIDGET_GTK) #include #include #include "gtkxtbin.h" #elif defined(MOZ_WIDGET_XLIB) #include "xlibxtbin.h" #include "xlibrgb.h" #endif //////////////////////////////////////////////////////////////////////// // CID's && IID's static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID); // needed for NS_TRY_SAFE_CALL_* static NS_DEFINE_IID(kCPluginManagerCID, NS_PLUGINMANAGER_CID); static NS_DEFINE_IID(kIPluginStreamListenerIID, NS_IPLUGINSTREAMLISTENER_IID); static NS_DEFINE_IID(kIPluginInstanceIID, NS_IPLUGININSTANCE_IID); static NS_DEFINE_IID(kIPluginTagInfoIID, NS_IPLUGINTAGINFO_IID); static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); /////////////////////////////////////////////////////////////////////////////// // ns4xPluginStreamListener Methods NS_IMPL_ISUPPORTS1(ns4xPluginStreamListener, nsIPluginStreamListener); /////////////////////////////////////////////////////////////////////////////// ns4xPluginStreamListener::ns4xPluginStreamListener(nsIPluginInstance* inst, void* notifyData, const char* aURL) : mNotifyData(notifyData), mStreamBuffer(nsnull), mNotifyURL(nsnull), mStreamStarted(PR_FALSE), mStreamCleanedUp(PR_FALSE), mCallNotify(PR_FALSE), mStreamInfo(nsnull) { NS_INIT_REFCNT(); mInst = (ns4xPluginInstance*) inst; mPosition = 0; mStreamBufferSize = 0; // Initialize the 4.x interface structure memset(&mNPStream, 0, sizeof(mNPStream)); NS_IF_ADDREF(mInst); if (aURL) mNotifyURL = PL_strdup(aURL); } /////////////////////////////////////////////////////////////////////////////// ns4xPluginStreamListener::~ns4xPluginStreamListener(void) { // remove itself from the instance stream list ns4xPluginInstance *inst = mInst; if(inst) { nsInstanceStream * prev = nsnull; for(nsInstanceStream *is = inst->mStreams; is != nsnull; is = is->mNext) { if(is->mPluginStreamListener == this) { if(prev == nsnull) inst->mStreams = is->mNext; else prev->mNext = is->mNext; delete is; break; } prev = is; } } // For those cases when NewStream is never called, we still may need to fire a // notification callback. Return network error as fallback reason because for other // cases, notify should have already been called for other reasons elsewhere. CallURLNotify(NPRES_NETWORK_ERR); // lets get rid of the buffer if (mStreamBuffer) { PR_Free(mStreamBuffer); mStreamBuffer=nsnull; } NS_IF_RELEASE(inst); if (mNotifyURL) PL_strfree(mNotifyURL); } /////////////////////////////////////////////////////////////////////////////// nsresult ns4xPluginStreamListener::CleanUpStream(NPReason reason) { nsresult rv = NS_ERROR_FAILURE; if(mStreamCleanedUp) return NS_OK; if(!mInst || !mInst->IsStarted()) return rv; const NPPluginFuncs *callbacks = nsnull; mInst->GetCallbacks(&callbacks); if(!callbacks) return rv; NPP npp; mInst->GetNPP(&npp); if (mStreamStarted && callbacks->destroystream != NULL) { PRLibrary* lib = nsnull; lib = mInst->fLibrary; NPError error; NS_TRY_SAFE_CALL_RETURN(error, CallNPP_DestroyStreamProc(callbacks->destroystream, npp, &mNPStream, reason), lib); NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("NPP DestroyStream called: this=%p, npp=%p, reason=%d, return=%d, url=%s\n", this, npp, reason, error, mNPStream.url)); if(error == NPERR_NO_ERROR) rv = NS_OK; } mStreamCleanedUp = PR_TRUE; mStreamStarted = PR_FALSE; // fire notification back to plugin, just like before CallURLNotify(reason); return rv; } /////////////////////////////////////////////////////////////////////////////// void ns4xPluginStreamListener::CallURLNotify(NPReason reason) { if(!mCallNotify || !mInst || !mInst->IsStarted()) return; mCallNotify = PR_FALSE; // only do this ONCE and prevent recursion const NPPluginFuncs *callbacks = nsnull; mInst->GetCallbacks(&callbacks); if(!callbacks) return; if (callbacks->urlnotify) { NPP npp; mInst->GetNPP(&npp); NS_TRY_SAFE_CALL_VOID(CallNPP_URLNotifyProc(callbacks->urlnotify, npp, mNotifyURL, reason, mNotifyData), mInst->fLibrary); NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("NPP URLNotify called: this=%p, npp=%p, notify=%p, reason=%d, url=%s\n", this, npp, mNotifyData, reason, mNotifyURL)); } // Let's not leak this stream listener. Release the reference to the stream listener // added for the notify callback in NewNotifyStream. // Note: This may destroy us if we are not being destroyed already. NS_RELEASE_THIS(); } /////////////////////////////////////////////////////////////////////////////// NS_IMETHODIMP ns4xPluginStreamListener::OnStartBinding(nsIPluginStreamInfo* pluginInfo) { if(!mInst) return NS_ERROR_FAILURE; NPP npp; const NPPluginFuncs *callbacks = nsnull; mInst->GetCallbacks(&callbacks); mInst->GetNPP(&npp); if(!callbacks || !mInst->IsStarted()) return NS_ERROR_FAILURE; PRBool seekable; nsMIMEType contentType; PRUint16 streamType = NP_NORMAL; NPError error; mNPStream.ndata = (void*) this; pluginInfo->GetURL(&mNPStream.url); mNPStream.notifyData = mNotifyData; pluginInfo->GetLength((PRUint32*)&(mNPStream.end)); pluginInfo->GetLastModified((PRUint32*)&(mNPStream.lastmodified)); pluginInfo->IsSeekable(&seekable); pluginInfo->GetContentType(&contentType); mStreamInfo = pluginInfo; // if we don't know the end of the stream, use 0 instead of -1. bug 59571 if (mNPStream.end == -1) mNPStream.end = 0; NS_TRY_SAFE_CALL_RETURN(error, CallNPP_NewStreamProc(callbacks->newstream, npp, (char *)contentType, &mNPStream, seekable, &streamType), mInst->fLibrary); NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("NPP NewStream called: this=%p, npp=%p, mime=%s, seek=%d, type=%d, return=%d, url=%s\n", this, npp, (char *)contentType, seekable, streamType, error, mNPStream.url)); if(error != NPERR_NO_ERROR) return NS_ERROR_FAILURE; // translate the old 4x style stream type to the new one switch(streamType) { case NP_NORMAL: mStreamType = nsPluginStreamType_Normal; break; case NP_ASFILEONLY: mStreamType = nsPluginStreamType_AsFileOnly; break; case NP_ASFILE: mStreamType = nsPluginStreamType_AsFile; break; case NP_SEEK: mStreamType = nsPluginStreamType_Seek; break; default: return NS_ERROR_FAILURE; } mStreamStarted = PR_TRUE; return NS_OK; } /////////////////////////////////////////////////////////////////////////////// NS_IMETHODIMP ns4xPluginStreamListener::OnDataAvailable(nsIPluginStreamInfo* pluginInfo, nsIInputStream* input, PRUint32 length) { nsresult rv = NS_ERROR_FAILURE; if (!mInst || !mInst->IsStarted()) return rv; const NPPluginFuncs *callbacks = nsnull; mInst->GetCallbacks(&callbacks); // check out if plugin implements NPP_Write call if(!callbacks || !callbacks->write || !length) return rv; // it'll cancel necko transaction if (!mStreamBuffer) { // to optimize the mem usage & performance we have to allocate mStreamBuffer here // in first ODA when length of data available in input stream is known. // mStreamBuffer will be freed in DTOR. // we also have to remember the size of that buff // to make safe consecutive Read() calls form input stream into our buff. if (length >= MAX_PLUGIN_NECKO_BUFFER) { // ">" is rare case for decoded stream, but lets eat it all mStreamBufferSize = length; } else { PRUint32 contentLength; pluginInfo->GetLength(&contentLength); if (contentLength < MAX_PLUGIN_NECKO_BUFFER) { // this is most common case for contentLength < 16k mStreamBufferSize = length < contentLength ? contentLength:length; } else { mStreamBufferSize = MAX_PLUGIN_NECKO_BUFFER; } } mStreamBuffer = (char*) PR_Malloc(mStreamBufferSize); if (!mStreamBuffer) return NS_ERROR_OUT_OF_MEMORY; } // prepare NPP_ calls params NPP npp; mInst->GetNPP(&npp); pluginInfo->GetURL(&mNPStream.url); pluginInfo->GetLastModified((PRUint32*)&(mNPStream.lastmodified)); PRInt32 streamOffset; pluginInfo->GetStreamOffset(&streamOffset); mPosition = streamOffset; streamOffset += length; // Set new stream offset for the next ODA call // regardless of how following NPP_Write call will behave // we pretend to consume all data from the input stream. // It's possible that current steam position will be overwritten // from NPP_RangeRequest call made from NPP_Write, so // we cannot call SetStreamOffset after NPP_Write. // Note: there is a special case when data flow // should be temporarily stopped if NPP_WriteReady returns 0 (bug #89270) pluginInfo->SetStreamOffset(streamOffset); PRUint32 bytesToRead = mStreamBufferSize; if (length < mStreamBufferSize) { // do not read more that supplier wants us to read bytesToRead = length; } do { PRInt32 amountRead = 0; rv = input->Read(mStreamBuffer, bytesToRead, (PRUint32*)&amountRead); if (amountRead == 0 || NS_FAILED(rv)) { NS_WARNING("input->Read() returns no data, it's almost impossible to get here"); break; } // this loop in general case will end on length <= 0, without extra input->Read() call length -= amountRead; char *ptrStreamBuffer = mStreamBuffer; // tmp ptr // it is possible plugin's NPP_Write() returns 0 byte consumed // we use zeroBytesWriteCount to count situation like this // and break the loop PRInt32 zeroBytesWriteCount = 0; // amountRead tells us how many bytes were put in the buffer // WriteReady returns to us how many bytes the plugin is // ready to handle - we have to keep calling WriteReady and // Write until amountRead > 0 for(;;) { // it breaks on (amountRead <= 0) or on error PRInt32 numtowrite; if (callbacks->writeready) { NS_TRY_SAFE_CALL_RETURN(numtowrite, CallNPP_WriteReadyProc(callbacks->writeready, npp, &mNPStream), mInst->fLibrary); NPP_PLUGIN_LOG(PLUGIN_LOG_NOISY, ("NPP WriteReady called: this=%p, npp=%p, return(towrite)=%d, url=%s\n", this, npp, numtowrite, mNPStream.url)); // if WriteReady returned 0, the plugin is not ready to handle // the data, return FAILURE for now if (numtowrite <= 0) { NS_ASSERTION(numtowrite,"WriteReady returned Zero"); rv = NS_ERROR_FAILURE; break; } if (numtowrite > amountRead) numtowrite = amountRead; } else { // if WriteReady is not supported by the plugin, // just write the whole buffer numtowrite = amountRead; } PRInt32 writeCount = 0; // bytes consumed by plugin instance NS_TRY_SAFE_CALL_RETURN(writeCount, CallNPP_WriteProc(callbacks->write, npp, &mNPStream, mPosition, numtowrite, (void *)ptrStreamBuffer), mInst->fLibrary); NPP_PLUGIN_LOG(PLUGIN_LOG_NOISY, ("NPP Write called: this=%p, npp=%p, pos=%d, len=%d, buf=%s, return(written)=%d, url=%s\n", this, npp, mPosition, numtowrite, (char *)ptrStreamBuffer, writeCount, mNPStream.url)); if (writeCount > 0) { mPosition += writeCount; amountRead -= writeCount; if (amountRead <= 0) break; // in common case we'll break for(;;) loop here zeroBytesWriteCount = 0; if (writeCount % sizeof(long)) { // memmove will take care about alignment memmove(ptrStreamBuffer,ptrStreamBuffer+writeCount,amountRead); } else { // if aligned we can use ptrStreamBuffer += to eliminate memmove() ptrStreamBuffer += writeCount; } } else if (writeCount == 0) { // if NPP_Write() returns writeCount == 0 lets say 3 times in a raw // lets consider this as end of ODA call (plugin isn't hungry, or broken) without an error. if (++zeroBytesWriteCount == 3) { length = 0; // break do{}while rv = NS_OK; break; } } else { length = 0; // break do{}while rv = NS_ERROR_FAILURE; break; } } // end of for(;;) } while ((PRInt32)(length) > 0); return rv; } /////////////////////////////////////////////////////////////////////////////// NS_IMETHODIMP ns4xPluginStreamListener::OnFileAvailable(nsIPluginStreamInfo* pluginInfo, const char* fileName) { if(!mInst || !mInst->IsStarted()) return NS_ERROR_FAILURE; NPP npp; const NPPluginFuncs *callbacks = nsnull; mInst->GetCallbacks(&callbacks); mInst->GetNPP(&npp); if(!callbacks) return NS_ERROR_FAILURE; pluginInfo->GetURL(&mNPStream.url); if (callbacks->asfile == NULL) return NS_OK; PRLibrary* lib = nsnull; lib = mInst->fLibrary; NS_TRY_SAFE_CALL_VOID(CallNPP_StreamAsFileProc(callbacks->asfile, npp, &mNPStream, fileName), lib); NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("NPP StreamAsFile called: this=%p, npp=%p, url=%s, file=%s\n", this, npp, mNPStream.url, fileName)); return NS_OK; } /////////////////////////////////////////////////////////////////////////////// NS_IMETHODIMP ns4xPluginStreamListener::OnStopBinding(nsIPluginStreamInfo* pluginInfo, nsresult status) { if(!mInst || !mInst->IsStarted()) return NS_ERROR_FAILURE; if(pluginInfo) { pluginInfo->GetURL(&mNPStream.url); pluginInfo->GetLastModified((PRUint32*)&(mNPStream.lastmodified)); } // check if the stream is of seekable type and later its destruction // see bug 91140 nsresult rv = NS_OK; if(mStreamType != nsPluginStreamType_Seek) { NPReason reason = NPRES_DONE; if (NS_FAILED(status)) reason = NPRES_NETWORK_ERR; // since the stream failed, we need to tell the plugin that rv = CleanUpStream(reason); } if(rv != NPERR_NO_ERROR) return NS_ERROR_FAILURE; return NS_OK; } NS_IMETHODIMP ns4xPluginStreamListener::GetStreamType(nsPluginStreamType *result) { *result = mStreamType; return NS_OK; } /////////////////////////////////////////////////////////////////////////////// nsInstanceStream::nsInstanceStream() { mNext = nsnull; mPluginStreamListener = nsnull; } /////////////////////////////////////////////////////////////////////////////// nsInstanceStream::~nsInstanceStream() { } /////////////////////////////////////////////////////////////////////////////// NS_IMPL_ISUPPORTS2(ns4xPluginInstance, nsIPluginInstance, nsIScriptablePlugin) /////////////////////////////////////////////////////////////////////////////// ns4xPluginInstance :: ns4xPluginInstance(NPPluginFuncs* callbacks, PRLibrary* aLibrary) : fCallbacks(callbacks) { NS_INIT_REFCNT(); NS_ASSERTION(fCallbacks != NULL, "null callbacks"); // Initialize the NPP structure. fNPP.pdata = NULL; fNPP.ndata = this; fLibrary = aLibrary; mWindowless = PR_FALSE; mTransparent = PR_FALSE; mStarted = PR_FALSE; mStreams = nsnull; mCached = PR_FALSE; PLUGIN_LOG(PLUGIN_LOG_BASIC, ("ns4xPluginInstance ctor: this=%p\n",this)); } /////////////////////////////////////////////////////////////////////////////// ns4xPluginInstance :: ~ns4xPluginInstance(void) { PLUGIN_LOG(PLUGIN_LOG_BASIC, ("ns4xPluginInstance dtor: this=%p\n",this)); #if defined(MOZ_WIDGET_GTK) if (mXtBin) gtk_widget_destroy(mXtBin); #elif defined(MOZ_WIDGET_XLIB) if (mXlibXtBin) { delete mXlibXtBin; } #endif // clean the stream list if any for(nsInstanceStream *is = mStreams; is != nsnull;) { nsInstanceStream * next = is->mNext; delete is; is = next; } } /////////////////////////////////////////////////////////////////////////////// PRBool ns4xPluginInstance :: IsStarted(void) { return mStarted; } //////////////////////////////////////////////////////////////////////// NS_IMETHODIMP ns4xPluginInstance::Initialize(nsIPluginInstancePeer* peer) { PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("ns4xPluginInstance::Initialize this=%p\n",this)); #ifdef MOZ_WIDGET_GTK mXtBin = nsnull; #elif defined(MOZ_WIDGET_XLIB) mXlibXtBin = nsnull; #endif return InitializePlugin(peer); } //////////////////////////////////////////////////////////////////////// NS_IMETHODIMP ns4xPluginInstance::GetPeer(nsIPluginInstancePeer* *resultingPeer) { *resultingPeer = mPeer; NS_IF_ADDREF(*resultingPeer); return NS_OK; } //////////////////////////////////////////////////////////////////////// NS_IMETHODIMP ns4xPluginInstance::Start(void) { PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("ns4xPluginInstance::Start this=%p\n",this)); #ifdef MOZ_WIDGET_XLIB if (mXlibXtBin == nsnull) mXlibXtBin = new xtbin(); if (mXlibXtBin == nsnull) return NS_ERROR_OUT_OF_MEMORY; if (!mXlibXtBin->xtbin_initialized()) mXlibXtBin->xtbin_init(); #ifdef NS_DEBUG printf("Made new XtBin: %p, %d\n", mXlibXtBin, mXlibXtBin->xtbin_initialized()); #endif #endif if(mStarted) return NS_OK; else return InitializePlugin(mPeer); } //////////////////////////////////////////////////////////////////////// NS_IMETHODIMP ns4xPluginInstance::Stop(void) { PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("ns4xPluginInstance::Stop this=%p\n",this)); NPError error; #if defined(MOZ_WIDGET_GTK) if (mXtBin) { gtk_widget_destroy(mXtBin); mXtBin = 0; } #elif defined(MOZ_WIDGET_XLIB) if (mXlibXtBin) { mXlibXtBin->xtbin_destroy(); mXlibXtBin = 0; } #endif if(!mStarted) return NS_OK; if (fCallbacks->destroy == NULL) return NS_ERROR_FAILURE; // XXX right error? NPSavedData *sdata = 0; // clean up open streams for(nsInstanceStream *is = mStreams; is != nsnull;) { ns4xPluginStreamListener * listener = is->mPluginStreamListener; nsInstanceStream *next = is->mNext; delete is; is = next; mStreams = is; // Clean up our stream after removing it from the list because // it may be released and destroyed at this point. if(listener) listener->CleanUpStream(NPRES_USER_BREAK); } NS_TRY_SAFE_CALL_RETURN(error, CallNPP_DestroyProc(fCallbacks->destroy, &fNPP, &sdata), fLibrary); NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("NPP Destroy called: this=%p, npp=%p, return=%d\n", this, &fNPP, error)); mStarted = PR_FALSE; if(error != NPERR_NO_ERROR) return NS_ERROR_FAILURE; else return NS_OK; } //////////////////////////////////////////////////////////////////////// nsresult ns4xPluginInstance::InitializePlugin(nsIPluginInstancePeer* peer) { NS_ENSURE_ARG_POINTER(peer); mPeer = peer; nsCOMPtr taginfo = do_QueryInterface(mPeer); NS_ENSURE_TRUE(taginfo, NS_ERROR_NO_INTERFACE); PRUint16 count = 0; const char* const* names = nsnull; const char* const* values = nsnull; nsPluginTagType tagtype; nsresult rv = taginfo->GetTagType(&tagtype); if (NS_SUCCEEDED(rv)) { // Note: If we failed to get the tag type, we may be a full page plugin, so no arguments rv = taginfo->GetAttributes(count, names, values); NS_ENSURE_SUCCESS(rv, rv); // nsPluginTagType_Object or Applet may also have PARAM tags // Note: The arrays handed back by GetParameters() are // crafted specially to be directly behind the arrays from GetAtributes() // with a null entry as a seperator. This is for 4.x backwards compatibility! // see bug 111008 for details if (tagtype != nsPluginTagType_Embed) { PRUint16 pcount = 0; const char* const* pnames = nsnull; const char* const* pvalues = nsnull; if (NS_SUCCEEDED(taginfo->GetParameters(pcount, pnames, pvalues))) { NS_ASSERTION(nsnull == values[count], "attribute/parameter array not setup correctly for 4.x plugins"); if (pcount) count += ++pcount; //if it's all setup correctly, then all we need is to change the count (attrs + PARAM/blank + params) } } } NS_ENSURE_TRUE(fCallbacks->newp, NS_ERROR_FAILURE); // XXX Note that the NPPluginType_* enums were crafted to be // backward compatible... nsPluginMode mode; nsMIMEType mimetype; NPError error; mPeer->GetMode(&mode); mPeer->GetMIMEType(&mimetype); NS_TRY_SAFE_CALL_RETURN(error, CallNPP_NewProc(fCallbacks->newp, (char *)mimetype, &fNPP, (PRUint16)mode, count, (char**)names, (char**)values, NULL), fLibrary); NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("NPP New called: this=%p, npp=%p, mime=%s, mode=%d, argc=%d, return=%d\n", this, &fNPP, mimetype, mode, count, error)); if(error != NPERR_NO_ERROR) rv = NS_ERROR_FAILURE; mStarted = PR_TRUE; return rv; } //////////////////////////////////////////////////////////////////////// NS_IMETHODIMP ns4xPluginInstance::Destroy(void) { PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("ns4xPluginInstance::Destroy this=%p\n",this)); // destruction is handled in the Stop call return NS_OK; } //////////////////////////////////////////////////////////////////////// NS_IMETHODIMP ns4xPluginInstance::SetWindow(nsPluginWindow* window) { #if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_XLIB) NPSetWindowCallbackStruct *ws; #endif // XXX 4.x plugins don't want a SetWindow(NULL). if (!window || !mStarted) return NS_OK; NPError error; #ifdef MOZ_WIDGET_GTK // bug 108337, flash plugin on linux doesn't like window->width <= 0 if ((PRInt32) window->width <= 0 || (PRInt32) window->height <= 0) return NS_OK; // Allocate and fill out the ws_info data if (!window->ws_info) { #ifdef NS_DEBUG printf("About to create new ws_info...\n"); #endif // allocate a new NPSetWindowCallbackStruct structure at ws_info window->ws_info = (NPSetWindowCallbackStruct *)PR_MALLOC(sizeof(NPSetWindowCallbackStruct)); if (!window->ws_info) return NS_ERROR_OUT_OF_MEMORY; ws = (NPSetWindowCallbackStruct *)window->ws_info; GdkWindow *win = gdk_window_lookup((XID)window->window); if (!win) return NS_ERROR_FAILURE; { #ifdef NS_DEBUG printf("About to create new xtbin of %i X %i from %p...\n", window->width, window->height, win); #endif #if 0 // if we destroyed the plugin when we left the page, we could remove this // code (i believe) the problem here is that the window gets destroyed when // its parent, etc does by changing a page the plugin instance is being // held on to, so when we return to the page, we have a mXtBin, but it is // in a not-so-good state. // -- // this is lame. we shouldn't be destroying this everytime, but I can't find // a good way to tell if we need to destroy/recreate the xtbin or not // what if the plugin wants to change the window and not just resize it?? // (pav) if (mXtBin) { gtk_widget_destroy(mXtBin); mXtBin = NULL; } #endif if (!mXtBin) { mXtBin = gtk_xtbin_new(win, 0); // Check to see if creating mXtBin failed for some reason. // if it did, we can't go any further. if (!mXtBin) return NS_ERROR_FAILURE; } gtk_widget_set_usize(mXtBin, window->width, window->height); #ifdef NS_DEBUG printf("About to show xtbin(%p)...\n", mXtBin); fflush(NULL); #endif gtk_widget_show(mXtBin); #ifdef NS_DEBUG printf("completed gtk_widget_show(%p)\n", mXtBin); fflush(NULL); #endif } // fill in window info structure ws->type = 0; // OK, that was a guess!! ws->depth = gdk_rgb_get_visual()->depth; ws->display = GTK_XTBIN(mXtBin)->xtdisplay; ws->visual = GDK_VISUAL_XVISUAL(gdk_rgb_get_visual()); ws->colormap = GDK_COLORMAP_XCOLORMAP(gdk_window_get_colormap(win)); XFlush(ws->display); } // !window->ws_info if (!mXtBin) return NS_ERROR_FAILURE; // And now point the NPWindow structures window // to the actual X window window->window = (nsPluginPort *)GTK_XTBIN(mXtBin)->xtwindow; gtk_xtbin_resize(mXtBin, window->width, window->height); #elif defined(MOZ_WIDGET_XLIB) // Allocate and fill out the ws_info data if (!window->ws_info) { #ifdef NS_DEBUG printf("About to create new ws_info...\n"); #endif // allocate a new NPSetWindowCallbackStruct structure at ws_info window->ws_info = (NPSetWindowCallbackStruct *)PR_MALLOC(sizeof(NPSetWindowCallbackStruct)); if (!window->ws_info) return NS_ERROR_OUT_OF_MEMORY; ws = (NPSetWindowCallbackStruct *)window->ws_info; #if 1 /* See comment above in GTK+ port ... */ if (mXlibXtBin) { delete mXlibXtBin; mXlibXtBin = nsnull; } #endif if (!mXlibXtBin) { mXlibXtBin = new xtbin(); // Check to see if creating mXlibXtBin failed for some reason. // if it did, we can't go any further. if (!mXlibXtBin) return NS_ERROR_FAILURE; } if (window->window) { #ifdef NS_DEBUG printf("About to create new xtbin of %i X %i from %08x...\n", window->width, window->height, window->window); #endif mXlibXtBin->xtbin_new((Window)window->window); mXlibXtBin->xtbin_resize(0, 0, window->width, window->height); #ifdef NS_DEBUG printf("About to show xtbin(%p)...\n", mXlibXtBin); fflush(NULL); #endif mXlibXtBin->xtbin_realize(); } /* Set window attributes */ XlibRgbHandle *xlibRgbHandle = xxlib_find_handle(XXLIBRGB_DEFAULT_HANDLE); Display *xdisplay = xxlib_rgb_get_display(xlibRgbHandle); /* Fill in window info structure */ ws->type = 0; ws->depth = xxlib_rgb_get_depth(xlibRgbHandle); ws->display = xdisplay; ws->visual = xxlib_rgb_get_visual(xlibRgbHandle); ws->colormap = xxlib_rgb_get_cmap(xlibRgbHandle); XFlush(ws->display); } // !window->ws_info // And now point the NPWindow structures window // to the actual X window window->window = (nsPluginPort *)mXlibXtBin->xtbin_xtwindow(); #endif // MOZ_WIDGET if (fCallbacks->setwindow) { // XXX Turns out that NPPluginWindow and NPWindow are structurally // identical (on purpose!), so there's no need to make a copy. PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("ns4xPluginInstance::SetWindow (about to call it) this=%p\n",this)); NS_TRY_SAFE_CALL_RETURN(error, CallNPP_SetWindowProc(fCallbacks->setwindow, &fNPP, (NPWindow*) window), fLibrary); NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("NPP SetWindow called: this=%p, [x=%d,y=%d,w=%d,h=%d], clip[t=%d,b=%d,l=%d,r=%d], return=%d\n", this, window->x, window->y, window->width, window->height, window->clipRect.top, window->clipRect.bottom, window->clipRect.left, window->clipRect.right, error)); // XXX In the old code, we'd just ignore any errors coming // back from the plugin's SetWindow(). Is this the correct // behavior?!? } return NS_OK; } //////////////////////////////////////////////////////////////////////// /* NOTE: the caller must free the stream listener */ // Create a normal stream, one without a urlnotify callback NS_IMETHODIMP ns4xPluginInstance::NewStream(nsIPluginStreamListener** listener) { return NewNotifyStream(listener, nsnull, PR_FALSE, nsnull); } //////////////////////////////////////////////////////////////////////// // Create a stream that will notify when complete nsresult ns4xPluginInstance::NewNotifyStream(nsIPluginStreamListener** listener, void* notifyData, PRBool aCallNotify, const char* aURL) { ns4xPluginStreamListener* stream = new ns4xPluginStreamListener(this, notifyData, aURL); NS_ENSURE_TRUE(stream, NS_ERROR_OUT_OF_MEMORY); // add it to the list nsInstanceStream * is = new nsInstanceStream(); NS_ENSURE_TRUE(is, NS_ERROR_OUT_OF_MEMORY); is->mNext = mStreams; is->mPluginStreamListener = stream; mStreams = is; stream->SetCallNotify(aCallNotify); // set flag in stream to call URLNotify NS_ADDREF(stream); // Stabilize nsresult res = stream->QueryInterface(kIPluginStreamListenerIID, (void**)listener); // Destabilize and avoid leaks. Avoid calling delete NS_RELEASE(stream); return res; } NS_IMETHODIMP ns4xPluginInstance::Print(nsPluginPrint* platformPrint) { NS_ENSURE_TRUE(platformPrint, NS_ERROR_NULL_POINTER); NPPrint* thePrint = (NPPrint *)platformPrint; // to be compatible with the older SDK versions and to match what // 4.x and other browsers do, overwrite |window.type| field with one // more copy of |platformPrint|. See bug 113264 if(fCallbacks) { PRUint16 sdkmajorversion = (fCallbacks->version & 0xff00)>>8; PRUint16 sdkminorversion = fCallbacks->version & 0x00ff; if((sdkmajorversion == 0) && (sdkminorversion < 11)) { // Let's copy platformPrint bytes over to where it was supposed to be // in older versions -- four bytes towards the beginning of the struct // but we should be careful about possible misalignments if(sizeof(NPWindowType) >= sizeof(void *)) { void* source = thePrint->print.embedPrint.platformPrint; void** destination = (void **)&(thePrint->print.embedPrint.window.type); *destination = source; } else NS_ASSERTION(PR_FALSE, "Incompatible OS for assignment"); } } NS_TRY_SAFE_CALL_VOID(CallNPP_PrintProc(fCallbacks->print, &fNPP, thePrint), fLibrary); NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("NPP PrintProc called: this=%p, pDC=%p, [x=%d,y=%d,w=%d,h=%d], clip[t=%d,b=%d,l=%d,r=%d]\n", this, platformPrint->print.embedPrint.platformPrint, platformPrint->print.embedPrint.window.x, platformPrint->print.embedPrint.window.y, platformPrint->print.embedPrint.window.width, platformPrint->print.embedPrint.window.height, platformPrint->print.embedPrint.window.clipRect.top, platformPrint->print.embedPrint.window.clipRect.bottom, platformPrint->print.embedPrint.window.clipRect.left, platformPrint->print.embedPrint.window.clipRect.right)); return NS_OK; } NS_IMETHODIMP ns4xPluginInstance::HandleEvent(nsPluginEvent* event, PRBool* handled) { if(!mStarted) return NS_OK; if (event == nsnull) return NS_ERROR_FAILURE; PRInt16 result = 0; if (fCallbacks->event) { #ifdef XP_MAC result = CallNPP_HandleEventProc(fCallbacks->event, &fNPP, (void*) event->event); #endif #if defined(XP_WIN) || defined(XP_OS2) NPEvent npEvent; npEvent.event = event->event; npEvent.wParam = event->wParam; npEvent.lParam = event->lParam; NS_TRY_SAFE_CALL_RETURN(result, CallNPP_HandleEventProc(fCallbacks->event, &fNPP, (void*)&npEvent), fLibrary); #endif NPP_PLUGIN_LOG(PLUGIN_LOG_NOISY, ("NPP HandleEvent called: this=%p, npp=%p, event=%d, return=%d\n", this, &fNPP, event->event, result)); *handled = result; } return NS_OK; } //////////////////////////////////////////////////////////////////////// NS_IMETHODIMP ns4xPluginInstance :: GetValue(nsPluginInstanceVariable variable, void *value) { nsresult res = NS_OK; switch (variable) { case nsPluginInstanceVariable_WindowlessBool: *(PRBool *)value = mWindowless; break; case nsPluginInstanceVariable_TransparentBool: *(PRBool *)value = mTransparent; break; case nsPluginInstanceVariable_DoCacheBool: *(PRBool *)value = mCached; break; case nsPluginInstanceVariable_CallSetWindowAfterDestroyBool: *(PRBool *)value = 0; // not supported for 4.x plugins break; default: if(fCallbacks->getvalue && mStarted) { NS_TRY_SAFE_CALL_RETURN(res, CallNPP_GetValueProc(fCallbacks->getvalue, &fNPP, (NPPVariable)variable, value), fLibrary); NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("NPP GetValue called: this=%p, npp=%p, var=%d, value=%d, return=%d\n", this, &fNPP, variable, value, res)); } } return res; } //////////////////////////////////////////////////////////////////////// nsresult ns4xPluginInstance::GetNPP(NPP* aNPP) { if(aNPP != nsnull) *aNPP = &fNPP; else return NS_ERROR_NULL_POINTER; return NS_OK; } //////////////////////////////////////////////////////////////////////// nsresult ns4xPluginInstance::GetCallbacks(const NPPluginFuncs ** aCallbacks) { if(aCallbacks != nsnull) *aCallbacks = fCallbacks; else return NS_ERROR_NULL_POINTER; return NS_OK; } //////////////////////////////////////////////////////////////////////// nsresult ns4xPluginInstance :: SetWindowless(PRBool aWindowless) { mWindowless = aWindowless; return NS_OK; } //////////////////////////////////////////////////////////////////////// nsresult ns4xPluginInstance :: SetTransparent(PRBool aTransparent) { mTransparent = aTransparent; return NS_OK; } //////////////////////////////////////////////////////////////////////// /* readonly attribute nsQIResult scriptablePeer; */ NS_IMETHODIMP ns4xPluginInstance :: GetScriptablePeer(void * *aScriptablePeer) { if (!aScriptablePeer) return NS_ERROR_NULL_POINTER; *aScriptablePeer = nsnull; return GetValue(nsPluginInstanceVariable_ScriptableInstance, aScriptablePeer); } //////////////////////////////////////////////////////////////////////// /* readonly attribute nsIIDPtr scriptableInterface; */ NS_IMETHODIMP ns4xPluginInstance :: GetScriptableInterface(nsIID * *aScriptableInterface) { if (!aScriptableInterface) return NS_ERROR_NULL_POINTER; *aScriptableInterface = nsnull; return GetValue(nsPluginInstanceVariable_ScriptableIID, (void*)aScriptableInterface); }