1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
23 * Tim Copperfield <timecop@network.email.ne.jp>
24 * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
45 #include "nsNPAPIPluginInstance.h"
46 #include "nsNPAPIPlugin.h"
47 #include "nsNPAPIPluginStreamListener.h"
48 #include "nsPluginHostImpl.h"
49 #include "nsPluginSafety.h"
50 #include "nsPluginLogging.h"
52 #include "nsPIPluginInstancePeer.h"
53 #include "nsPIDOMWindow.h"
54 #include "nsIDocument.h"
56 #include "nsJSNPRuntime.h"
59 #include "nsILegacyPluginWrapperOS2.h"
62 static NS_DEFINE_IID(kCPluginManagerCID
, NS_PLUGINMANAGER_CID
); // needed for NS_TRY_SAFE_CALL
63 static NS_DEFINE_IID(kIPluginStreamListenerIID
, NS_IPLUGINSTREAMLISTENER_IID
);
65 // nsNPAPIPluginStreamListener Methods
67 NS_IMPL_ISUPPORTS3(nsNPAPIPluginStreamListener
, nsIPluginStreamListener
,
68 nsITimerCallback
, nsIHTTPHeaderListener
)
70 nsNPAPIPluginStreamListener::nsNPAPIPluginStreamListener(nsNPAPIPluginInstance
* inst
,
73 : mNotifyData(notifyData
),
74 mStreamBuffer(nsnull
),
75 mNotifyURL(aURL
? PL_strdup(aURL
) : nsnull
),
78 mStreamBufferByteCount(0),
79 mStreamType(nsPluginStreamType_Normal
),
80 mStreamStarted(PR_FALSE
),
81 mStreamCleanedUp(PR_FALSE
),
82 mCallNotify(PR_FALSE
),
83 mIsSuspended(PR_FALSE
),
84 mIsPluginInitJSStream(mInst
->mInPluginInitCall
&&
85 aURL
&& strncmp(aURL
, "javascript:",
86 sizeof("javascript:") - 1) == 0),
87 mResponseHeaderBuf(nsnull
)
89 memset(&mNPStream
, 0, sizeof(mNPStream
));
94 nsNPAPIPluginStreamListener::~nsNPAPIPluginStreamListener(void)
96 // remove itself from the instance stream list
97 nsNPAPIPluginInstance
*inst
= mInst
;
99 nsInstanceStream
* prev
= nsnull
;
100 for (nsInstanceStream
*is
= inst
->mStreams
; is
!= nsnull
; is
= is
->mNext
) {
101 if (is
->mPluginStreamListener
== this) {
103 inst
->mStreams
= is
->mNext
;
105 prev
->mNext
= is
->mNext
;
114 // For those cases when NewStream is never called, we still may need
115 // to fire a notification callback. Return network error as fallback
116 // reason because for other cases, notify should have already been
117 // called for other reasons elsewhere.
118 CallURLNotify(NPRES_NETWORK_ERR
);
120 // lets get rid of the buffer
122 PR_Free(mStreamBuffer
);
123 mStreamBuffer
=nsnull
;
129 PL_strfree(mNotifyURL
);
131 if (mResponseHeaderBuf
)
132 PL_strfree(mResponseHeaderBuf
);
135 nsresult
nsNPAPIPluginStreamListener::CleanUpStream(NPReason reason
)
137 nsresult rv
= NS_ERROR_FAILURE
;
139 if (mStreamCleanedUp
)
142 if (!mInst
|| !mInst
->IsStarted())
145 PluginDestructionGuard
guard(mInst
);
147 const NPPluginFuncs
*callbacks
= nsnull
;
148 mInst
->GetCallbacks(&callbacks
);
155 if (mStreamStarted
&& callbacks
->destroystream
) {
156 PRLibrary
* lib
= nsnull
;
157 lib
= mInst
->fLibrary
;
159 NS_TRY_SAFE_CALL_RETURN(error
, (*callbacks
->destroystream
)(npp
, &mNPStream
, reason
), lib
, mInst
);
161 NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL
,
162 ("NPP DestroyStream called: this=%p, npp=%p, reason=%d, return=%d, url=%s\n",
163 this, npp
, reason
, error
, mNPStream
.url
));
165 if (error
== NPERR_NO_ERROR
)
169 mStreamCleanedUp
= PR_TRUE
;
170 mStreamStarted
= PR_FALSE
;
174 // fire notification back to plugin, just like before
175 CallURLNotify(reason
);
180 void nsNPAPIPluginStreamListener::CallURLNotify(NPReason reason
)
182 if (!mCallNotify
|| !mInst
|| !mInst
->IsStarted())
185 PluginDestructionGuard
guard(mInst
);
187 mCallNotify
= PR_FALSE
; // only do this ONCE and prevent recursion
189 const NPPluginFuncs
*callbacks
= nsnull
;
190 mInst
->GetCallbacks(&callbacks
);
194 if (callbacks
->urlnotify
) {
199 NS_TRY_SAFE_CALL_VOID((*callbacks
->urlnotify
)(npp
, mNotifyURL
, reason
, mNotifyData
), mInst
->fLibrary
, mInst
);
201 NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL
,
202 ("NPP URLNotify called: this=%p, npp=%p, notify=%p, reason=%d, url=%s\n",
203 this, npp
, mNotifyData
, reason
, mNotifyURL
));
208 nsNPAPIPluginStreamListener::OnStartBinding(nsIPluginStreamInfo
* pluginInfo
)
211 return NS_ERROR_FAILURE
;
213 PluginDestructionGuard
guard(mInst
);
216 const NPPluginFuncs
*callbacks
= nsnull
;
218 mInst
->GetCallbacks(&callbacks
);
221 if (!callbacks
|| !mInst
->IsStarted())
222 return NS_ERROR_FAILURE
;
225 nsMIMEType contentType
;
226 PRUint16 streamType
= NP_NORMAL
;
229 mNPStream
.ndata
= (void*) this;
230 pluginInfo
->GetURL(&mNPStream
.url
);
231 mNPStream
.notifyData
= mNotifyData
;
233 pluginInfo
->GetLength((PRUint32
*)&(mNPStream
.end
));
234 pluginInfo
->GetLastModified((PRUint32
*)&(mNPStream
.lastmodified
));
235 pluginInfo
->IsSeekable(&seekable
);
236 pluginInfo
->GetContentType(&contentType
);
238 if (!mResponseHeaders
.IsEmpty()) {
239 mResponseHeaderBuf
= PL_strdup(mResponseHeaders
.get());
240 mNPStream
.headers
= mResponseHeaderBuf
;
243 mStreamInfo
= pluginInfo
;
245 NS_TRY_SAFE_CALL_RETURN(error
, (*callbacks
->newstream
)(npp
, (char*)contentType
, &mNPStream
, seekable
, &streamType
), mInst
->fLibrary
, mInst
);
247 NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL
,
248 ("NPP NewStream called: this=%p, npp=%p, mime=%s, seek=%d, type=%d, return=%d, url=%s\n",
249 this, npp
, (char *)contentType
, seekable
, streamType
, error
, mNPStream
.url
));
251 if (error
!= NPERR_NO_ERROR
)
252 return NS_ERROR_FAILURE
;
257 mStreamType
= nsPluginStreamType_Normal
;
260 mStreamType
= nsPluginStreamType_AsFileOnly
;
263 mStreamType
= nsPluginStreamType_AsFile
;
266 mStreamType
= nsPluginStreamType_Seek
;
269 return NS_ERROR_FAILURE
;
272 mStreamStarted
= PR_TRUE
;
277 nsNPAPIPluginStreamListener::SuspendRequest()
279 NS_ASSERTION(!mIsSuspended
,
280 "Suspending a request that's already suspended!");
282 nsCOMPtr
<nsINPAPIPluginStreamInfo
> pluginInfoNPAPI
=
283 do_QueryInterface(mStreamInfo
);
286 if (!pluginInfoNPAPI
|| !(request
= pluginInfoNPAPI
->GetRequest())) {
287 NS_ERROR("Trying to suspend a non-suspendable stream!");
288 return NS_ERROR_FAILURE
;
291 nsresult rv
= StartDataPump();
292 NS_ENSURE_SUCCESS(rv
, rv
);
294 mIsSuspended
= PR_TRUE
;
296 return request
->Suspend();
300 nsNPAPIPluginStreamListener::ResumeRequest()
302 nsCOMPtr
<nsINPAPIPluginStreamInfo
> pluginInfoNPAPI
=
303 do_QueryInterface(mStreamInfo
);
305 nsIRequest
*request
= pluginInfoNPAPI
->GetRequest();
307 // request can be null if the network stream is done.
311 mIsSuspended
= PR_FALSE
;
315 nsNPAPIPluginStreamListener::StartDataPump()
318 mDataPumpTimer
= do_CreateInstance("@mozilla.org/timer;1", &rv
);
319 NS_ENSURE_SUCCESS(rv
, rv
);
321 // Start pumping data to the plugin every 100ms until it obeys and
323 return mDataPumpTimer
->InitWithCallback(this, 100,
324 nsITimer::TYPE_REPEATING_SLACK
);
328 nsNPAPIPluginStreamListener::StopDataPump()
330 if (mDataPumpTimer
) {
331 mDataPumpTimer
->Cancel();
332 mDataPumpTimer
= nsnull
;
336 // Return true if a javascript: load that was started while the plugin
337 // was being initialized is still in progress.
339 nsNPAPIPluginStreamListener::PluginInitJSLoadInProgress()
341 for (nsInstanceStream
*is
= mInst
->mStreams
; is
; is
= is
->mNext
) {
342 if (is
->mPluginStreamListener
->mIsPluginInitJSStream
) {
350 // This method is called when there's more data available off the
351 // network, but it's also called from our data pump when we're feeding
352 // the plugin data that we already got off the network, but the plugin
353 // was unable to consume it at the point it arrived. In the case when
354 // the plugin pump calls this method, the input argument will be null,
355 // and the length will be the number of bytes available in our
358 nsNPAPIPluginStreamListener::OnDataAvailable(nsIPluginStreamInfo
* pluginInfo
,
359 nsIInputStream
* input
,
362 if (!mInst
|| !mInst
->IsStarted())
363 return NS_ERROR_FAILURE
;
365 PluginDestructionGuard
guard(mInst
);
367 // Just in case the caller switches plugin info on us.
368 mStreamInfo
= pluginInfo
;
370 const NPPluginFuncs
*callbacks
= nsnull
;
371 mInst
->GetCallbacks(&callbacks
);
372 // check out if plugin implements NPP_Write call
373 if (!callbacks
|| !callbacks
->write
|| !length
)
374 return NS_ERROR_FAILURE
; // it'll cancel necko transaction
376 if (!mStreamBuffer
) {
377 // To optimize the mem usage & performance we have to allocate
378 // mStreamBuffer here in first ODA when length of data available
379 // in input stream is known. mStreamBuffer will be freed in DTOR.
380 // we also have to remember the size of that buff to make safe
381 // consecutive Read() calls form input stream into our buff.
383 PRUint32 contentLength
;
384 pluginInfo
->GetLength(&contentLength
);
386 mStreamBufferSize
= PR_MAX(length
, contentLength
);
388 // Limit the size of the initial buffer to MAX_PLUGIN_NECKO_BUFFER
389 // (16k). This buffer will grow if needed, as in the case where
390 // we're getting data faster than the plugin can process it.
391 mStreamBufferSize
= PR_MIN(mStreamBufferSize
, MAX_PLUGIN_NECKO_BUFFER
);
393 mStreamBuffer
= (char*) PR_Malloc(mStreamBufferSize
);
395 return NS_ERROR_OUT_OF_MEMORY
;
398 // prepare NPP_ calls params
402 PRInt32 streamPosition
;
403 pluginInfo
->GetStreamOffset(&streamPosition
);
404 PRInt32 streamOffset
= streamPosition
;
407 streamOffset
+= length
;
409 // Set new stream offset for the next ODA call regardless of how
410 // following NPP_Write call will behave we pretend to consume all
411 // data from the input stream. It's possible that current steam
412 // position will be overwritten from NPP_RangeRequest call made
413 // from NPP_Write, so we cannot call SetStreamOffset after
416 // Note: there is a special case when data flow should be
417 // temporarily stopped if NPP_WriteReady returns 0 (bug #89270)
418 pluginInfo
->SetStreamOffset(streamOffset
);
420 // set new end in case the content is compressed
421 // initial end is less than end of decompressed stream
422 // and some plugins (e.g. acrobat) can fail.
423 if ((PRInt32
)mNPStream
.end
< streamOffset
)
424 mNPStream
.end
= streamOffset
;
428 while (NS_SUCCEEDED(rv
) && length
> 0) {
429 if (input
&& length
) {
430 if (mStreamBufferSize
< mStreamBufferByteCount
+ length
&& mIsSuspended
) {
431 // We're in the ::OnDataAvailable() call that we might get
432 // after suspending a request, or we suspended the request
433 // from within this ::OnDataAvailable() call while there's
434 // still data in the input, and we don't have enough space to
435 // store what we got off the network. Reallocate our internal
437 mStreamBufferSize
= mStreamBufferByteCount
+ length
;
438 char *buf
= (char*)PR_Realloc(mStreamBuffer
, mStreamBufferSize
);
440 return NS_ERROR_OUT_OF_MEMORY
;
445 PRUint32 bytesToRead
=
446 PR_MIN(length
, mStreamBufferSize
- mStreamBufferByteCount
);
448 PRUint32 amountRead
= 0;
449 rv
= input
->Read(mStreamBuffer
+ mStreamBufferByteCount
, bytesToRead
,
451 NS_ENSURE_SUCCESS(rv
, rv
);
453 if (amountRead
== 0) {
454 NS_NOTREACHED("input->Read() returns no data, it's almost impossible "
460 mStreamBufferByteCount
+= amountRead
;
461 length
-= amountRead
;
463 // No input, nothing to read. Set length to 0 so that we don't
464 // keep iterating through this outer loop any more.
469 // Temporary pointer to the beginning of the data we're writing as
470 // we loop and feed the plugin data.
471 char *ptrStreamBuffer
= mStreamBuffer
;
473 // it is possible plugin's NPP_Write() returns 0 byte consumed. We
474 // use zeroBytesWriteCount to count situation like this and break
476 PRInt32 zeroBytesWriteCount
= 0;
478 // mStreamBufferByteCount tells us how many bytes there are in the
479 // buffer. WriteReady returns to us how many bytes the plugin is
481 while (mStreamBufferByteCount
> 0) {
483 if (callbacks
->writeready
) {
484 NS_TRY_SAFE_CALL_RETURN(numtowrite
, (*callbacks
->writeready
)(npp
, &mNPStream
), mInst
->fLibrary
, mInst
);
485 NPP_PLUGIN_LOG(PLUGIN_LOG_NOISY
,
486 ("NPP WriteReady called: this=%p, npp=%p, "
487 "return(towrite)=%d, url=%s\n",
488 this, npp
, numtowrite
, mNPStream
.url
));
490 if (!mStreamStarted
) {
491 // The plugin called NPN_DestroyStream() from within
492 // NPP_WriteReady(), kill the stream.
494 return NS_BINDING_ABORTED
;
497 // if WriteReady returned 0, the plugin is not ready to handle
498 // the data, suspend the stream (if it isn't already
501 // Also suspend the stream if the stream we're loading is not
502 // a javascript: URL load that was initiated during plugin
503 // initialization and there currently is such a stream
504 // loading. This is done to work around a Windows Media Player
505 // plugin bug where it can't deal with being fed data for
506 // other streams while it's waiting for data from the
507 // javascript: URL loads it requests during
508 // initialization. See bug 386493 for more details.
510 if (numtowrite
<= 0 ||
511 (!mIsPluginInitJSStream
&& PluginInitJSLoadInProgress())) {
513 rv
= SuspendRequest();
516 // Break out of the inner loop, but keep going through the
517 // outer loop in case there's more data to read from the
523 numtowrite
= PR_MIN(numtowrite
, mStreamBufferByteCount
);
525 // if WriteReady is not supported by the plugin, just write
527 numtowrite
= mStreamBufferByteCount
;
530 PRInt32 writeCount
= 0; // bytes consumed by plugin instance
531 NS_TRY_SAFE_CALL_RETURN(writeCount
, (*callbacks
->write
)(npp
, &mNPStream
, streamPosition
, numtowrite
, ptrStreamBuffer
), mInst
->fLibrary
, mInst
);
533 NPP_PLUGIN_LOG(PLUGIN_LOG_NOISY
,
534 ("NPP Write called: this=%p, npp=%p, pos=%d, len=%d, "
535 "buf=%s, return(written)=%d, url=%s\n",
536 this, npp
, streamPosition
, numtowrite
,
537 ptrStreamBuffer
, writeCount
, mNPStream
.url
));
539 if (!mStreamStarted
) {
540 // The plugin called NPN_DestroyStream() from within
541 // NPP_Write(), kill the stream.
542 return NS_BINDING_ABORTED
;
545 if (writeCount
> 0) {
546 NS_ASSERTION(writeCount
<= mStreamBufferByteCount
,
547 "Plugin read past the end of the available data!");
549 writeCount
= PR_MIN(writeCount
, mStreamBufferByteCount
);
550 mStreamBufferByteCount
-= writeCount
;
552 streamPosition
+= writeCount
;
554 zeroBytesWriteCount
= 0;
556 if (mStreamBufferByteCount
> 0) {
557 // This alignment code is most likely bogus, but we'll leave
558 // it in for now in case it matters for some plugins on some
559 // architectures. Who knows...
560 if (writeCount
% sizeof(PRWord
)) {
561 // memmove will take care about alignment
562 memmove(mStreamBuffer
, ptrStreamBuffer
+ writeCount
,
563 mStreamBufferByteCount
);
564 ptrStreamBuffer
= mStreamBuffer
;
566 // if aligned we can use ptrStreamBuffer += to eliminate
568 ptrStreamBuffer
+= writeCount
;
571 } else if (writeCount
== 0) {
572 // if NPP_Write() returns writeCount == 0 lets say 3 times in
573 // a row, suspend the request and continue feeding the plugin
574 // the data we got so far. Once that data is consumed, we'll
575 // resume the request.
576 if (mIsSuspended
|| ++zeroBytesWriteCount
== 3) {
578 rv
= SuspendRequest();
581 // Break out of the for loop, but keep going through the
582 // while loop in case there's more data to read from the
588 // Something's really wrong, kill the stream.
589 rv
= NS_ERROR_FAILURE
;
593 } // end of inner while loop
595 if (mStreamBufferByteCount
&& mStreamBuffer
!= ptrStreamBuffer
) {
596 memmove(mStreamBuffer
, ptrStreamBuffer
, mStreamBufferByteCount
);
600 if (streamPosition
!= streamOffset
) {
601 // The plugin didn't consume all available data, or consumed some
602 // of our cached data while we're pumping cached data. Adjust the
603 // plugin info's stream offset to match reality, except if the
604 // plugin info's stream offset was set by a re-entering
605 // NPN_RequestRead() call.
607 PRInt32 postWriteStreamPosition
;
608 pluginInfo
->GetStreamOffset(&postWriteStreamPosition
);
610 if (postWriteStreamPosition
== streamOffset
) {
611 pluginInfo
->SetStreamOffset(streamPosition
);
619 nsNPAPIPluginStreamListener::OnFileAvailable(nsIPluginStreamInfo
* pluginInfo
,
620 const char* fileName
)
622 if (!mInst
|| !mInst
->IsStarted())
623 return NS_ERROR_FAILURE
;
625 PluginDestructionGuard
guard(mInst
);
627 const NPPluginFuncs
*callbacks
= nsnull
;
628 mInst
->GetCallbacks(&callbacks
);
629 if (!callbacks
|| !callbacks
->asfile
)
630 return NS_ERROR_FAILURE
;
635 PRLibrary
* lib
= nsnull
;
636 lib
= mInst
->fLibrary
;
638 NS_TRY_SAFE_CALL_VOID((*callbacks
->asfile
)(npp
, &mNPStream
, fileName
), lib
, mInst
);
640 NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL
,
641 ("NPP StreamAsFile called: this=%p, npp=%p, url=%s, file=%s\n",
642 this, npp
, mNPStream
.url
, fileName
));
648 nsNPAPIPluginStreamListener::OnStopBinding(nsIPluginStreamInfo
* pluginInfo
,
653 if (NS_FAILED(status
)) {
654 // The stream was destroyed, or died for some reason. Make sure we
655 // cancel the underlying request.
656 nsCOMPtr
<nsINPAPIPluginStreamInfo
> pluginInfoNPAPI
=
657 do_QueryInterface(mStreamInfo
);
660 if (pluginInfoNPAPI
&& (request
= pluginInfoNPAPI
->GetRequest())) {
661 request
->Cancel(status
);
665 if (!mInst
|| !mInst
->IsStarted())
666 return NS_ERROR_FAILURE
;
668 // check if the stream is of seekable type and later its destruction
671 if (mStreamType
!= nsPluginStreamType_Seek
) {
672 NPReason reason
= NPRES_DONE
;
674 if (NS_FAILED(status
))
675 reason
= NPRES_NETWORK_ERR
; // since the stream failed, we need to tell the plugin that
677 rv
= CleanUpStream(reason
);
680 if (rv
!= NPERR_NO_ERROR
)
681 return NS_ERROR_FAILURE
;
687 nsNPAPIPluginStreamListener::GetStreamType(nsPluginStreamType
*result
)
689 *result
= mStreamType
;
694 nsNPAPIPluginStreamListener::Notify(nsITimer
*aTimer
)
696 NS_ASSERTION(aTimer
== mDataPumpTimer
, "Uh, wrong timer?");
698 PRInt32 oldStreamBufferByteCount
= mStreamBufferByteCount
;
700 nsresult rv
= OnDataAvailable(mStreamInfo
, nsnull
, mStreamBufferByteCount
);
703 // We ran into an error, no need to keep firing this timer then.
708 if (mStreamBufferByteCount
!= oldStreamBufferByteCount
&&
709 ((mStreamStarted
&& mStreamBufferByteCount
< 1024) ||
710 mStreamBufferByteCount
== 0)) {
711 // The plugin read some data and we've got less than 1024 bytes in
712 // our buffer (or its empty and the stream is already
713 // done). Resume the request so that we get more data off the
716 // Necko will pump data now that we've resumed the request.
724 nsNPAPIPluginStreamListener::StatusLine(const char* line
)
726 mResponseHeaders
.Append(line
);
727 mResponseHeaders
.Append('\n');
732 nsNPAPIPluginStreamListener::NewResponseHeader(const char* headerName
,
733 const char* headerValue
)
735 mResponseHeaders
.Append(headerName
);
736 mResponseHeaders
.Append(": ");
737 mResponseHeaders
.Append(headerValue
);
738 mResponseHeaders
.Append('\n');
742 nsInstanceStream::nsInstanceStream()
745 mPluginStreamListener
= nsnull
;
748 nsInstanceStream::~nsInstanceStream()
752 NS_IMPL_ISUPPORTS3(nsNPAPIPluginInstance
, nsIPluginInstance
, nsIScriptablePlugin
,
753 nsIPluginInstanceInternal
)
755 nsNPAPIPluginInstance::nsNPAPIPluginInstance(NPPluginFuncs
* callbacks
,
757 : fCallbacks(callbacks
),
759 #ifdef NP_NO_QUICKDRAW
760 mDrawingModel(NPDrawingModelCoreGraphics
),
762 mDrawingModel(NPDrawingModelQuickDraw
),
765 mWindowless(PR_FALSE
),
766 mTransparent(PR_FALSE
),
769 mIsJavaPlugin(PR_FALSE
),
770 mWantsAllNetworkStreams(PR_FALSE
),
771 mInPluginInitCall(PR_FALSE
),
775 NS_ASSERTION(fCallbacks
!= NULL
, "null callbacks");
777 // Initialize the NPP structure.
782 PLUGIN_LOG(PLUGIN_LOG_BASIC
, ("nsNPAPIPluginInstance ctor: this=%p\n",this));
785 nsNPAPIPluginInstance::~nsNPAPIPluginInstance(void)
787 PLUGIN_LOG(PLUGIN_LOG_BASIC
, ("nsNPAPIPluginInstance dtor: this=%p\n",this));
789 // clean the stream list if any
790 for (nsInstanceStream
*is
= mStreams
; is
!= nsnull
;) {
791 nsInstanceStream
* next
= is
->mNext
;
798 nsNPAPIPluginInstance::IsStarted(void)
803 NS_IMETHODIMP
nsNPAPIPluginInstance::Initialize(nsIPluginInstancePeer
* peer
)
805 PLUGIN_LOG(PLUGIN_LOG_NORMAL
, ("nsNPAPIPluginInstance::Initialize this=%p\n",this));
807 return InitializePlugin(peer
);
810 NS_IMETHODIMP
nsNPAPIPluginInstance::GetPeer(nsIPluginInstancePeer
* *resultingPeer
)
812 *resultingPeer
= mPeer
;
813 NS_IF_ADDREF(*resultingPeer
);
818 NS_IMETHODIMP
nsNPAPIPluginInstance::Start(void)
820 PLUGIN_LOG(PLUGIN_LOG_NORMAL
, ("nsNPAPIPluginInstance::Start this=%p\n",this));
825 return InitializePlugin(mPeer
);
828 NS_IMETHODIMP
nsNPAPIPluginInstance::Stop(void)
830 PLUGIN_LOG(PLUGIN_LOG_NORMAL
, ("nsNPAPIPluginInstance::Stop this=%p\n",this));
834 // Make sure the plugin didn't leave popups enabled.
835 if (mPopupStates
.Count() > 0) {
836 nsCOMPtr
<nsPIDOMWindow
> window
= GetDOMWindow();
839 window
->PopPopupControlState(openAbused
);
846 // If there's code from this plugin instance on the stack, delay the
848 if (PluginDestructionGuard::DelayDestroy(this)) {
852 // Make sure we lock while we're writing to mStarted after we've
853 // started as other threads might be checking that inside a lock.
854 EnterAsyncPluginThreadCallLock();
856 ExitAsyncPluginThreadCallLock();
858 OnPluginDestroy(&fNPP
);
860 if (fCallbacks
->destroy
== NULL
)
861 return NS_ERROR_FAILURE
;
863 NPSavedData
*sdata
= 0;
865 // clean up open streams
866 for (nsInstanceStream
*is
= mStreams
; is
!= nsnull
;) {
867 nsNPAPIPluginStreamListener
* listener
= is
->mPluginStreamListener
;
869 nsInstanceStream
*next
= is
->mNext
;
874 // Clean up our stream after removing it from the list because
875 // it may be released and destroyed at this point.
877 listener
->CleanUpStream(NPRES_USER_BREAK
);
880 NS_TRY_SAFE_CALL_RETURN(error
, (*fCallbacks
->destroy
)(&fNPP
, &sdata
), fLibrary
, this);
882 NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL
,
883 ("NPP Destroy called: this=%p, npp=%p, return=%d\n", this, &fNPP
, error
));
885 nsJSNPRuntime::OnPluginDestroy(&fNPP
);
887 if (error
!= NPERR_NO_ERROR
)
888 return NS_ERROR_FAILURE
;
893 already_AddRefed
<nsPIDOMWindow
>
894 nsNPAPIPluginInstance::GetDOMWindow()
896 nsCOMPtr
<nsPIPluginInstancePeer
> pp (do_QueryInterface(mPeer
));
900 nsCOMPtr
<nsIPluginInstanceOwner
> owner
;
901 pp
->GetOwner(getter_AddRefs(owner
));
905 nsCOMPtr
<nsIDocument
> doc
;
906 owner
->GetDocument(getter_AddRefs(doc
));
910 nsPIDOMWindow
*window
= doc
->GetWindow();
911 NS_IF_ADDREF(window
);
916 nsresult
nsNPAPIPluginInstance::InitializePlugin(nsIPluginInstancePeer
* peer
)
918 NS_ENSURE_ARG_POINTER(peer
);
920 nsCOMPtr
<nsIPluginTagInfo2
> taginfo
= do_QueryInterface(peer
);
921 NS_ENSURE_TRUE(taginfo
, NS_ERROR_NO_INTERFACE
);
923 PluginDestructionGuard
guard(this);
926 const char* const* names
= nsnull
;
927 const char* const* values
= nsnull
;
928 nsPluginTagType tagtype
;
929 nsresult rv
= taginfo
->GetTagType(&tagtype
);
930 if (NS_SUCCEEDED(rv
)) {
931 // Note: If we failed to get the tag type, we may be a full page plugin, so no arguments
932 rv
= taginfo
->GetAttributes(count
, names
, values
);
933 NS_ENSURE_SUCCESS(rv
, rv
);
935 // nsPluginTagType_Object or Applet may also have PARAM tags
936 // Note: The arrays handed back by GetParameters() are
937 // crafted specially to be directly behind the arrays from GetAttributes()
938 // with a null entry as a separator. This is for 4.x backwards compatibility!
939 // see bug 111008 for details
940 if (tagtype
!= nsPluginTagType_Embed
) {
942 const char* const* pnames
= nsnull
;
943 const char* const* pvalues
= nsnull
;
944 if (NS_SUCCEEDED(taginfo
->GetParameters(pcount
, pnames
, pvalues
))) {
945 NS_ASSERTION(!values
[count
], "attribute/parameter array not setup correctly for NPAPI plugins");
947 count
+= ++pcount
; // if it's all setup correctly, then all we need is to
948 // change the count (attrs + PARAM/blank + params)
953 NS_ENSURE_TRUE(fCallbacks
->newp
, NS_ERROR_FAILURE
);
955 // XXX Note that the NPPluginType_* enums were crafted to be
956 // backward compatible...
962 peer
->GetMode(&mode
);
963 peer
->GetMIMEType(&mimetype
);
965 // Some older versions of Flash have a bug in them
966 // that causes the stack to become currupt if we
967 // pass swliveconect=1 in the NPP_NewProc arrays.
968 // See bug 149336 (UNIX), bug 186287 (Mac)
970 // The code below disables the attribute unless
971 // the environment variable:
972 // MOZILLA_PLUGIN_DISABLE_FLASH_SWLIVECONNECT_HACK
975 // It is okay to disable this attribute because
976 // back in 4.x, scripting required liveconnect to
977 // start Java which was slow. Scripting no longer
978 // requires starting Java and is quick plus controled
979 // from the browser, so Flash now ignores this attribute.
981 // This code can not be put at the time of creating
982 // the array because we may need to examine the
983 // stream header to determine we want Flash.
985 static const char flashMimeType
[] = "application/x-shockwave-flash";
986 static const char blockedParam
[] = "swliveconnect";
987 if (count
&& !PL_strcasecmp(mimetype
, flashMimeType
)) {
988 static int cachedDisableHack
= 0;
989 if (!cachedDisableHack
) {
990 if (PR_GetEnv("MOZILLA_PLUGIN_DISABLE_FLASH_SWLIVECONNECT_HACK"))
991 cachedDisableHack
= -1;
993 cachedDisableHack
= 1;
995 if (cachedDisableHack
> 0) {
996 for (PRUint16 i
=0; i
<count
; i
++) {
997 if (!PL_strcasecmp(names
[i
], blockedParam
)) {
999 // I'm ugly casting |const char*| to |char*| and altering it
1000 // because I know we do malloc it values in
1001 // http://bonsai.mozilla.org/cvsblame.cgi?file=mozilla/layout/html/base/src/nsObjectFrame.cpp&rev=1.349&root=/cvsroot#3020
1002 // and free it at line #2096, so it couldn't be a const ptr to string literal
1003 char *val
= (char*) values
[i
];
1005 // we cannot just *val=0, it won't be free properly in such case
1015 mIsJavaPlugin
= nsPluginHostImpl::IsJavaMIMEType(mimetype
);
1017 // Assign mPeer now and mark this instance as started before calling NPP_New
1018 // because the plugin may call other NPAPI functions, like NPN_GetURLNotify,
1019 // that assume these are set before returning. If the plugin returns failure,
1020 // we'll clear them out below.
1024 PRBool oldVal
= mInPluginInitCall
;
1025 mInPluginInitCall
= PR_TRUE
;
1027 NS_TRY_SAFE_CALL_RETURN(error
, (*fCallbacks
->newp
)((char*)mimetype
, &fNPP
, (PRUint16
)mode
, count
, (char**)names
, (char**)values
, NULL
), fLibrary
,this);
1029 mInPluginInitCall
= oldVal
;
1031 NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL
,
1032 ("NPP New called: this=%p, npp=%p, mime=%s, mode=%d, argc=%d, return=%d\n",
1033 this, &fNPP
, mimetype
, mode
, count
, error
));
1035 if (error
!= NPERR_NO_ERROR
) {
1036 // since the plugin returned failure, these should not be set
1038 mStarted
= PR_FALSE
;
1040 return NS_ERROR_FAILURE
;
1046 NS_IMETHODIMP
nsNPAPIPluginInstance::Destroy(void)
1048 PLUGIN_LOG(PLUGIN_LOG_NORMAL
, ("nsNPAPIPluginInstance::Destroy this=%p\n",this));
1050 // destruction is handled in the Stop call
1054 NS_IMETHODIMP
nsNPAPIPluginInstance::SetWindow(nsPluginWindow
* window
)
1056 // XXX NPAPI plugins don't want a SetWindow(NULL).
1057 if (!window
|| !mStarted
)
1062 #if defined (MOZ_WIDGET_GTK2)
1063 // bug 108347, flash plugin on linux doesn't like window->width <=
1064 // 0, but Java needs wants this call.
1065 if (!mIsJavaPlugin
&& window
->type
== nsPluginWindowType_Window
&&
1066 (window
->width
<= 0 || window
->height
<= 0)) {
1069 #endif // MOZ_WIDGET
1071 if (fCallbacks
->setwindow
) {
1072 PluginDestructionGuard
guard(this);
1074 // XXX Turns out that NPPluginWindow and NPWindow are structurally
1075 // identical (on purpose!), so there's no need to make a copy.
1077 PLUGIN_LOG(PLUGIN_LOG_NORMAL
, ("nsNPAPIPluginInstance::SetWindow (about to call it) this=%p\n",this));
1079 PRBool oldVal
= mInPluginInitCall
;
1080 mInPluginInitCall
= PR_TRUE
;
1082 NS_TRY_SAFE_CALL_RETURN(error
, (*fCallbacks
->setwindow
)(&fNPP
, (NPWindow
*)window
), fLibrary
, this);
1084 mInPluginInitCall
= oldVal
;
1086 NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL
,
1087 ("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",
1088 this, window
->x
, window
->y
, window
->width
, window
->height
,
1089 window
->clipRect
.top
, window
->clipRect
.bottom
, window
->clipRect
.left
, window
->clipRect
.right
, error
));
1091 // XXX In the old code, we'd just ignore any errors coming
1092 // back from the plugin's SetWindow(). Is this the correct
1098 /* NOTE: the caller must free the stream listener */
1099 // Create a normal stream, one without a urlnotify callback
1100 NS_IMETHODIMP
nsNPAPIPluginInstance::NewStream(nsIPluginStreamListener
** listener
)
1102 return NewNotifyStream(listener
, nsnull
, PR_FALSE
, nsnull
);
1105 // Create a stream that will notify when complete
1106 nsresult
nsNPAPIPluginInstance::NewNotifyStream(nsIPluginStreamListener
** listener
,
1111 nsNPAPIPluginStreamListener
* stream
= new nsNPAPIPluginStreamListener(this, notifyData
, aURL
);
1112 NS_ENSURE_TRUE(stream
, NS_ERROR_OUT_OF_MEMORY
);
1114 // add it to the list
1115 nsInstanceStream
* is
= new nsInstanceStream();
1116 NS_ENSURE_TRUE(is
, NS_ERROR_OUT_OF_MEMORY
);
1118 is
->mNext
= mStreams
;
1119 is
->mPluginStreamListener
= stream
;
1121 stream
->SetCallNotify(aCallNotify
); // set flag in stream to call URLNotify
1123 NS_ADDREF(stream
); // Stabilize
1125 nsresult res
= stream
->QueryInterface(kIPluginStreamListenerIID
, (void**)listener
);
1127 // Destabilize and avoid leaks. Avoid calling delete <interface pointer>
1133 NS_IMETHODIMP
nsNPAPIPluginInstance::Print(nsPluginPrint
* platformPrint
)
1135 NS_ENSURE_TRUE(platformPrint
, NS_ERROR_NULL_POINTER
);
1137 PluginDestructionGuard
guard(this);
1139 NPPrint
* thePrint
= (NPPrint
*)platformPrint
;
1141 // to be compatible with the older SDK versions and to match what
1142 // NPAPI and other browsers do, overwrite |window.type| field with one
1143 // more copy of |platformPrint|. See bug 113264
1145 PRUint16 sdkmajorversion
= (fCallbacks
->version
& 0xff00)>>8;
1146 PRUint16 sdkminorversion
= fCallbacks
->version
& 0x00ff;
1147 if ((sdkmajorversion
== 0) && (sdkminorversion
< 11)) {
1148 // Let's copy platformPrint bytes over to where it was supposed to be
1149 // in older versions -- four bytes towards the beginning of the struct
1150 // but we should be careful about possible misalignments
1151 if (sizeof(NPWindowType
) >= sizeof(void *)) {
1152 void* source
= thePrint
->print
.embedPrint
.platformPrint
;
1153 void** destination
= (void **)&(thePrint
->print
.embedPrint
.window
.type
);
1154 *destination
= source
;
1157 NS_ASSERTION(PR_FALSE
, "Incompatible OS for assignment");
1161 if (fCallbacks
->print
)
1162 NS_TRY_SAFE_CALL_VOID((*fCallbacks
->print
)(&fNPP
, thePrint
), fLibrary
, this);
1164 NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL
,
1165 ("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",
1167 platformPrint
->print
.embedPrint
.platformPrint
,
1168 platformPrint
->print
.embedPrint
.window
.x
,
1169 platformPrint
->print
.embedPrint
.window
.y
,
1170 platformPrint
->print
.embedPrint
.window
.width
,
1171 platformPrint
->print
.embedPrint
.window
.height
,
1172 platformPrint
->print
.embedPrint
.window
.clipRect
.top
,
1173 platformPrint
->print
.embedPrint
.window
.clipRect
.bottom
,
1174 platformPrint
->print
.embedPrint
.window
.clipRect
.left
,
1175 platformPrint
->print
.embedPrint
.window
.clipRect
.right
));
1180 NS_IMETHODIMP
nsNPAPIPluginInstance::HandleEvent(nsPluginEvent
* event
, PRBool
* handled
)
1186 return NS_ERROR_FAILURE
;
1188 PluginDestructionGuard
guard(this);
1192 if (fCallbacks
->event
) {
1194 result
= (*fCallbacks
->event
)(&fNPP
, (void*)event
->event
);
1196 #elif defined(XP_WIN) || defined(XP_OS2)
1198 npEvent
.event
= event
->event
;
1199 npEvent
.wParam
= event
->wParam
;
1200 npEvent
.lParam
= event
->lParam
;
1202 NS_TRY_SAFE_CALL_RETURN(result
, (*fCallbacks
->event
)(&fNPP
, (void*)&npEvent
), fLibrary
, this);
1204 #else // MOZ_X11 or other
1205 result
= (*fCallbacks
->event
)(&fNPP
, (void*)&event
->event
);
1208 NPP_PLUGIN_LOG(PLUGIN_LOG_NOISY
,
1209 ("NPP HandleEvent called: this=%p, npp=%p, event=%d, return=%d\n",
1210 this, &fNPP
, event
->event
, result
));
1218 nsresult
nsNPAPIPluginInstance::GetValueInternal(NPPVariable variable
, void* value
)
1220 nsresult res
= NS_OK
;
1221 if (fCallbacks
->getvalue
&& mStarted
) {
1222 PluginDestructionGuard
guard(this);
1224 NS_TRY_SAFE_CALL_RETURN(res
, (*fCallbacks
->getvalue
)(&fNPP
, variable
, value
), fLibrary
, this);
1225 NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL
,
1226 ("NPP GetValue called: this=%p, npp=%p, var=%d, value=%d, return=%d\n",
1227 this, &fNPP
, variable
, value
, res
));
1230 /* Query interface for legacy Flash plugin */
1231 if (res
== NS_OK
&& variable
== NPPVpluginScriptableInstance
) {
1232 nsCOMPtr
<nsILegacyPluginWrapperOS2
> wrapper
=
1233 do_GetService(NS_LEGACY_PLUGIN_WRAPPER_CONTRACTID
, &res
);
1235 nsIID
*iid
= nsnull
;
1236 res
= (*fCallbacks
->getvalue
)(&fNPP
, NPPVpluginScriptableIID
, (void *)&iid
);
1238 res
= wrapper
->MaybeWrap(*iid
, *(nsISupports
**)value
, (nsISupports
**)value
);
1247 NS_IMETHODIMP
nsNPAPIPluginInstance::GetValue(nsPluginInstanceVariable variable
, void *value
)
1249 nsresult res
= NS_OK
;
1252 case nsPluginInstanceVariable_WindowlessBool
:
1253 *(PRBool
*)value
= mWindowless
;
1256 case nsPluginInstanceVariable_TransparentBool
:
1257 *(PRBool
*)value
= mTransparent
;
1260 case nsPluginInstanceVariable_DoCacheBool
:
1261 *(PRBool
*)value
= mCached
;
1264 case nsPluginInstanceVariable_CallSetWindowAfterDestroyBool
:
1265 *(PRBool
*)value
= 0; // not supported for 4.x plugins
1269 case nsPluginInstanceVariable_DrawingModel
:
1270 *(NPDrawingModel
*)value
= mDrawingModel
;
1275 res
= GetValueInternal((NPPVariable
)variable
, value
);
1281 nsresult
nsNPAPIPluginInstance::GetNPP(NPP
* aNPP
)
1286 return NS_ERROR_NULL_POINTER
;
1291 nsresult
nsNPAPIPluginInstance::GetCallbacks(const NPPluginFuncs
** aCallbacks
)
1294 *aCallbacks
= fCallbacks
;
1296 return NS_ERROR_NULL_POINTER
;
1301 NPError
nsNPAPIPluginInstance::SetWindowless(PRBool aWindowless
)
1303 mWindowless
= aWindowless
;
1304 return NPERR_NO_ERROR
;
1307 NPError
nsNPAPIPluginInstance::SetTransparent(PRBool aTransparent
)
1309 mTransparent
= aTransparent
;
1310 return NPERR_NO_ERROR
;
1313 NPError
nsNPAPIPluginInstance::SetWantsAllNetworkStreams(PRBool aWantsAllNetworkStreams
)
1315 mWantsAllNetworkStreams
= aWantsAllNetworkStreams
;
1316 return NPERR_NO_ERROR
;
1320 void nsNPAPIPluginInstance::SetDrawingModel(NPDrawingModel aModel
)
1322 mDrawingModel
= aModel
;
1325 NPDrawingModel
nsNPAPIPluginInstance::GetDrawingModel()
1327 return mDrawingModel
;
1331 /* readonly attribute nsQIResult scriptablePeer; */
1332 NS_IMETHODIMP
nsNPAPIPluginInstance::GetScriptablePeer(void * *aScriptablePeer
)
1334 if (!aScriptablePeer
)
1335 return NS_ERROR_NULL_POINTER
;
1337 *aScriptablePeer
= nsnull
;
1338 return GetValueInternal(NPPVpluginScriptableInstance
, aScriptablePeer
);
1341 /* readonly attribute nsIIDPtr scriptableInterface; */
1342 NS_IMETHODIMP
nsNPAPIPluginInstance::GetScriptableInterface(nsIID
* *aScriptableInterface
)
1344 if (!aScriptableInterface
)
1345 return NS_ERROR_NULL_POINTER
;
1347 *aScriptableInterface
= nsnull
;
1348 return GetValueInternal(NPPVpluginScriptableIID
, (void*)aScriptableInterface
);
1352 nsNPAPIPluginInstance::GetJSObject(JSContext
*cx
)
1354 JSObject
*obj
= nsnull
;
1355 NPObject
*npobj
= nsnull
;
1357 nsresult rv
= GetValueInternal(NPPVpluginScriptableNPObject
, &npobj
);
1359 if (NS_SUCCEEDED(rv
) && npobj
) {
1360 obj
= nsNPObjWrapper::GetNewOrUsed(&fNPP
, cx
, npobj
);
1362 _releaseobject(npobj
);
1369 nsNPAPIPluginInstance::DefineJavaProperties()
1371 NPObject
*plugin_obj
= nsnull
;
1373 // The dummy Java plugin's scriptable object is what we want to
1374 // expose as window.Packages. And Window.Packages.java will be
1375 // exposed as window.java.
1377 // Get the scriptable plugin object.
1378 nsresult rv
= GetValueInternal(NPPVpluginScriptableNPObject
, &plugin_obj
);
1380 if (NS_FAILED(rv
) || !plugin_obj
) {
1384 // Get the NPObject wrapper for window.
1385 NPObject
*window_obj
= _getwindowobject(&fNPP
);
1388 _releaseobject(plugin_obj
);
1393 NPIdentifier java_id
= _getstringidentifier("java");
1394 NPIdentifier packages_id
= _getstringidentifier("Packages");
1396 NPObject
*java_obj
= nsnull
;
1398 OBJECT_TO_NPVARIANT(plugin_obj
, v
);
1400 // Define the properties.
1402 bool ok
= _setproperty(&fNPP
, window_obj
, packages_id
, &v
);
1404 ok
= _getproperty(&fNPP
, plugin_obj
, java_id
, &v
);
1406 if (ok
&& NPVARIANT_IS_OBJECT(v
)) {
1407 // Set java_obj so that we properly release it at the end of
1409 java_obj
= NPVARIANT_TO_OBJECT(v
);
1411 ok
= _setproperty(&fNPP
, window_obj
, java_id
, &v
);
1415 _releaseobject(window_obj
);
1416 _releaseobject(plugin_obj
);
1417 _releaseobject(java_obj
);
1421 nsNPAPIPluginInstance::GetFormValue(nsAString
& aValue
)
1425 char *value
= nsnull
;
1426 nsresult rv
= GetValueInternal(NPPVformValue
, &value
);
1428 if (NS_SUCCEEDED(rv
) && value
) {
1429 CopyUTF8toUTF16(value
, aValue
);
1431 // NPPVformValue allocates with NPN_MemAlloc(), which uses
1433 nsMemory::Free(value
);
1440 nsNPAPIPluginInstance::PushPopupsEnabledState(PRBool aEnabled
)
1442 nsCOMPtr
<nsPIDOMWindow
> window
= GetDOMWindow();
1446 PopupControlState oldState
=
1447 window
->PushPopupControlState(aEnabled
? openAllowed
: openAbused
,
1450 if (!mPopupStates
.AppendElement(NS_INT32_TO_PTR(oldState
))) {
1451 // Appending to our state stack failed, push what we just popped.
1452 window
->PopPopupControlState(oldState
);
1457 nsNPAPIPluginInstance::PopPopupsEnabledState()
1459 PRInt32 last
= mPopupStates
.Count() - 1;
1466 nsCOMPtr
<nsPIDOMWindow
> window
= GetDOMWindow();
1470 PopupControlState oldState
=
1471 (PopupControlState
)NS_PTR_TO_INT32(mPopupStates
[last
]);
1473 window
->PopPopupControlState(oldState
);
1475 mPopupStates
.RemoveElementAt(last
);
1479 nsNPAPIPluginInstance::GetPluginAPIVersion()
1481 return fCallbacks
->version
;