1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "PluginAsyncSurrogate.h"
8 
9 #include "base/message_loop.h"
10 #include "base/message_pump_default.h"
11 #include "mozilla/dom/ContentChild.h"
12 #include "mozilla/plugins/PluginInstanceParent.h"
13 #include "mozilla/plugins/PluginModuleParent.h"
14 #include "mozilla/plugins/PluginScriptableObjectParent.h"
15 #include "mozilla/Telemetry.h"
16 #include "nsJSNPRuntime.h"
17 #include "nsNPAPIPlugin.h"
18 #include "nsNPAPIPluginInstance.h"
19 #include "nsNPAPIPluginStreamListener.h"
20 #include "nsPluginInstanceOwner.h"
21 #include "nsPluginStreamListenerPeer.h"
22 #include "npruntime.h"
23 #include "nsThreadUtils.h"
24 #include "PluginMessageUtils.h"
25 
26 namespace mozilla {
27 namespace plugins {
28 
AsyncNPObject(PluginAsyncSurrogate * aSurrogate)29 AsyncNPObject::AsyncNPObject(PluginAsyncSurrogate* aSurrogate)
30   : NPObject()
31   , mSurrogate(aSurrogate)
32   , mRealObject(nullptr)
33 {
34 }
35 
~AsyncNPObject()36 AsyncNPObject::~AsyncNPObject()
37 {
38   if (mRealObject) {
39     --mRealObject->asyncWrapperCount;
40     parent::_releaseobject(mRealObject);
41     mRealObject = nullptr;
42   }
43 }
44 
45 NPObject*
GetRealObject()46 AsyncNPObject::GetRealObject()
47 {
48   if (mRealObject) {
49     return mRealObject;
50   }
51   PluginInstanceParent* instance = PluginInstanceParent::Cast(mSurrogate->GetNPP());
52   if (!instance) {
53     return nullptr;
54   }
55   NPObject* realObject = nullptr;
56   NPError err = instance->NPP_GetValue(NPPVpluginScriptableNPObject,
57                                        &realObject);
58   if (err != NPERR_NO_ERROR) {
59     return nullptr;
60   }
61   if (realObject->_class != PluginScriptableObjectParent::GetClass()) {
62     NS_ERROR("Don't know what kind of object this is!");
63     parent::_releaseobject(realObject);
64     return nullptr;
65   }
66   mRealObject = static_cast<ParentNPObject*>(realObject);
67   ++mRealObject->asyncWrapperCount;
68   return mRealObject;
69 }
70 
71 class MOZ_STACK_CLASS RecursionGuard
72 {
73 public:
RecursionGuard()74   RecursionGuard()
75     : mIsRecursive(sHasEntered)
76   {
77     if (!mIsRecursive) {
78       sHasEntered = true;
79     }
80   }
81 
~RecursionGuard()82   ~RecursionGuard()
83   {
84     if (!mIsRecursive) {
85       sHasEntered = false;
86     }
87   }
88 
89   inline bool
IsRecursive()90   IsRecursive()
91   {
92     return mIsRecursive;
93   }
94 
95 private:
96   bool        mIsRecursive;
97   static bool sHasEntered;
98 };
99 
100 bool RecursionGuard::sHasEntered = false;
101 
PluginAsyncSurrogate(PluginModuleParent * aParent)102 PluginAsyncSurrogate::PluginAsyncSurrogate(PluginModuleParent* aParent)
103   : mParent(aParent)
104   , mMode(0)
105   , mWindow(nullptr)
106   , mAcceptCalls(false)
107   , mInstantiated(false)
108   , mAsyncSetWindow(false)
109   , mInitCancelled(false)
110   , mDestroyPending(false)
111   , mAsyncCallsInFlight(0)
112 {
113   MOZ_ASSERT(aParent);
114 }
115 
~PluginAsyncSurrogate()116 PluginAsyncSurrogate::~PluginAsyncSurrogate()
117 {
118 }
119 
120 bool
Init(NPMIMEType aPluginType,NPP aInstance,uint16_t aMode,int16_t aArgc,char * aArgn[],char * aArgv[])121 PluginAsyncSurrogate::Init(NPMIMEType aPluginType, NPP aInstance, uint16_t aMode,
122                            int16_t aArgc, char* aArgn[], char* aArgv[])
123 {
124   mMimeType = aPluginType;
125   nsNPAPIPluginInstance* instance =
126     static_cast<nsNPAPIPluginInstance*>(aInstance->ndata);
127   MOZ_ASSERT(instance);
128   mInstance = instance;
129   mMode = aMode;
130   for (int i = 0; i < aArgc; ++i) {
131     mNames.AppendElement(NullableString(aArgn[i]));
132     mValues.AppendElement(NullableString(aArgv[i]));
133   }
134   return true;
135 }
136 
137 /* static */ bool
Create(PluginModuleParent * aParent,NPMIMEType aPluginType,NPP aInstance,uint16_t aMode,int16_t aArgc,char * aArgn[],char * aArgv[])138 PluginAsyncSurrogate::Create(PluginModuleParent* aParent, NPMIMEType aPluginType,
139                              NPP aInstance, uint16_t aMode, int16_t aArgc,
140                              char* aArgn[], char* aArgv[])
141 {
142   RefPtr<PluginAsyncSurrogate> surrogate(new PluginAsyncSurrogate(aParent));
143   if (!surrogate->Init(aPluginType, aInstance, aMode, aArgc, aArgn, aArgv)) {
144     return false;
145   }
146   PluginAsyncSurrogate* rawSurrogate = nullptr;
147   surrogate.forget(&rawSurrogate);
148   aInstance->pdata = static_cast<PluginDataResolver*>(rawSurrogate);
149   return true;
150 }
151 
152 /* static */ PluginAsyncSurrogate*
Cast(NPP aInstance)153 PluginAsyncSurrogate::Cast(NPP aInstance)
154 {
155   MOZ_ASSERT(aInstance);
156   PluginDataResolver* resolver =
157     reinterpret_cast<PluginDataResolver*>(aInstance->pdata);
158   if (!resolver) {
159     return nullptr;
160   }
161   return resolver->GetAsyncSurrogate();
162 }
163 
164 nsresult
NPP_New(NPError * aError)165 PluginAsyncSurrogate::NPP_New(NPError* aError)
166 {
167   if (!mInstance) {
168     return NS_ERROR_NULL_POINTER;
169   }
170 
171   nsresult rv = mParent->NPP_NewInternal(mMimeType.BeginWriting(), GetNPP(),
172                                          mMode, mNames, mValues, nullptr,
173                                          aError);
174   if (NS_FAILED(rv)) {
175     return rv;
176   }
177   return NS_OK;
178 }
179 
180 void
NP_GetEntryPoints(NPPluginFuncs * aFuncs)181 PluginAsyncSurrogate::NP_GetEntryPoints(NPPluginFuncs* aFuncs)
182 {
183   aFuncs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
184   aFuncs->destroy = &NPP_Destroy;
185   aFuncs->getvalue = &NPP_GetValue;
186   aFuncs->setvalue = &NPP_SetValue;
187   aFuncs->newstream = &NPP_NewStream;
188   aFuncs->setwindow = &NPP_SetWindow;
189   aFuncs->writeready = &NPP_WriteReady;
190   aFuncs->event = &NPP_HandleEvent;
191   aFuncs->destroystream = &NPP_DestroyStream;
192   // We need to set these so that content code doesn't make assumptions
193   // about these operations not being supported
194   aFuncs->write = &PluginModuleParent::NPP_Write;
195   aFuncs->asfile = &PluginModuleParent::NPP_StreamAsFile;
196 }
197 
198 /* static */ void
NotifyDestroyPending(NPP aInstance)199 PluginAsyncSurrogate::NotifyDestroyPending(NPP aInstance)
200 {
201   PluginAsyncSurrogate* surrogate = Cast(aInstance);
202   if (!surrogate) {
203     return;
204   }
205   surrogate->NotifyDestroyPending();
206 }
207 
208 NPP
GetNPP()209 PluginAsyncSurrogate::GetNPP()
210 {
211   MOZ_ASSERT(mInstance);
212   NPP npp;
213   DebugOnly<nsresult> rv = mInstance->GetNPP(&npp);
214   MOZ_ASSERT(NS_SUCCEEDED(rv));
215   return npp;
216 }
217 
218 void
NotifyDestroyPending()219 PluginAsyncSurrogate::NotifyDestroyPending()
220 {
221   mDestroyPending = true;
222   nsJSNPRuntime::OnPluginDestroyPending(GetNPP());
223 }
224 
225 NPError
NPP_Destroy(NPSavedData ** aSave)226 PluginAsyncSurrogate::NPP_Destroy(NPSavedData** aSave)
227 {
228   NotifyDestroyPending();
229   if (!WaitForInit()) {
230     return NPERR_GENERIC_ERROR;
231   }
232   return PluginModuleParent::NPP_Destroy(GetNPP(), aSave);
233 }
234 
235 NPError
NPP_GetValue(NPPVariable aVariable,void * aRetval)236 PluginAsyncSurrogate::NPP_GetValue(NPPVariable aVariable, void* aRetval)
237 {
238   if (aVariable != NPPVpluginScriptableNPObject) {
239     if (!WaitForInit()) {
240       return NPERR_GENERIC_ERROR;
241     }
242 
243     PluginInstanceParent* instance = PluginInstanceParent::Cast(GetNPP());
244     MOZ_ASSERT(instance);
245     return instance->NPP_GetValue(aVariable, aRetval);
246   }
247 
248   NPObject* npobject = parent::_createobject(GetNPP(),
249                                              const_cast<NPClass*>(GetClass()));
250   MOZ_ASSERT(npobject);
251   MOZ_ASSERT(npobject->_class == GetClass());
252   MOZ_ASSERT(npobject->referenceCount == 1);
253   *(NPObject**)aRetval = npobject;
254   return npobject ? NPERR_NO_ERROR : NPERR_GENERIC_ERROR;
255 }
256 
257 NPError
NPP_SetValue(NPNVariable aVariable,void * aValue)258 PluginAsyncSurrogate::NPP_SetValue(NPNVariable aVariable, void* aValue)
259 {
260   if (!WaitForInit()) {
261     return NPERR_GENERIC_ERROR;
262   }
263   return PluginModuleParent::NPP_SetValue(GetNPP(), aVariable, aValue);
264 }
265 
266 NPError
NPP_NewStream(NPMIMEType aType,NPStream * aStream,NPBool aSeekable,uint16_t * aStype)267 PluginAsyncSurrogate::NPP_NewStream(NPMIMEType aType, NPStream* aStream,
268                                     NPBool aSeekable, uint16_t* aStype)
269 {
270   mPendingNewStreamCalls.AppendElement(PendingNewStreamCall(aType, aStream,
271                                                             aSeekable));
272   if (aStype) {
273     *aStype = nsPluginStreamListenerPeer::STREAM_TYPE_UNKNOWN;
274   }
275   return NPERR_NO_ERROR;
276 }
277 
278 NPError
NPP_SetWindow(NPWindow * aWindow)279 PluginAsyncSurrogate::NPP_SetWindow(NPWindow* aWindow)
280 {
281   mWindow = aWindow;
282   mAsyncSetWindow = false;
283   return NPERR_NO_ERROR;
284 }
285 
286 nsresult
AsyncSetWindow(NPWindow * aWindow)287 PluginAsyncSurrogate::AsyncSetWindow(NPWindow* aWindow)
288 {
289   mWindow = aWindow;
290   mAsyncSetWindow = true;
291   return NS_OK;
292 }
293 
294 void
NPP_Print(NPPrint * aPrintInfo)295 PluginAsyncSurrogate::NPP_Print(NPPrint* aPrintInfo)
296 {
297   // Do nothing, we've got nothing to print right now
298 }
299 
300 int16_t
NPP_HandleEvent(void * event)301 PluginAsyncSurrogate::NPP_HandleEvent(void* event)
302 {
303   // Drop the event -- the plugin isn't around to handle it
304   return false;
305 }
306 
307 int32_t
NPP_WriteReady(NPStream * aStream)308 PluginAsyncSurrogate::NPP_WriteReady(NPStream* aStream)
309 {
310   // We'll tell the browser to retry in a bit. Eventually NPP_WriteReady
311   // will resolve to the plugin's NPP_WriteReady and this should all just work.
312   return 0;
313 }
314 
315 NPError
NPP_DestroyStream(NPStream * aStream,NPReason aReason)316 PluginAsyncSurrogate::NPP_DestroyStream(NPStream* aStream, NPReason aReason)
317 {
318   for (uint32_t idx = 0, len = mPendingNewStreamCalls.Length(); idx < len; ++idx) {
319     PendingNewStreamCall& curPendingCall = mPendingNewStreamCalls[idx];
320     if (curPendingCall.mStream == aStream) {
321       mPendingNewStreamCalls.RemoveElementAt(idx);
322       break;
323     }
324   }
325   return NPERR_NO_ERROR;
326 }
327 
328 /* static */ NPError
NPP_Destroy(NPP aInstance,NPSavedData ** aSave)329 PluginAsyncSurrogate::NPP_Destroy(NPP aInstance, NPSavedData** aSave)
330 {
331   PluginAsyncSurrogate* rawSurrogate = Cast(aInstance);
332   MOZ_ASSERT(rawSurrogate);
333   PluginModuleParent* module = rawSurrogate->GetParent();
334   if (module && !module->IsInitialized()) {
335     // Take ownership of pdata's surrogate since we're going to release it
336     RefPtr<PluginAsyncSurrogate> surrogate(dont_AddRef(rawSurrogate));
337     aInstance->pdata = nullptr;
338     // We haven't actually called NPP_New yet, so we should remove the
339     // surrogate for this instance.
340     bool removeOk = module->RemovePendingSurrogate(surrogate);
341     MOZ_ASSERT(removeOk);
342     if (!removeOk) {
343       return NPERR_GENERIC_ERROR;
344     }
345     surrogate->mInitCancelled = true;
346     return NPERR_NO_ERROR;
347   }
348   return rawSurrogate->NPP_Destroy(aSave);
349 }
350 
351 /* static */ NPError
NPP_GetValue(NPP aInstance,NPPVariable aVariable,void * aRetval)352 PluginAsyncSurrogate::NPP_GetValue(NPP aInstance, NPPVariable aVariable,
353                                    void* aRetval)
354 {
355   PluginAsyncSurrogate* surrogate = Cast(aInstance);
356   MOZ_ASSERT(surrogate);
357   return surrogate->NPP_GetValue(aVariable, aRetval);
358 }
359 
360 /* static */ NPError
NPP_SetValue(NPP aInstance,NPNVariable aVariable,void * aValue)361 PluginAsyncSurrogate::NPP_SetValue(NPP aInstance, NPNVariable aVariable,
362                                    void* aValue)
363 {
364   PluginAsyncSurrogate* surrogate = Cast(aInstance);
365   MOZ_ASSERT(surrogate);
366   return surrogate->NPP_SetValue(aVariable, aValue);
367 }
368 
369 /* static */ NPError
NPP_NewStream(NPP aInstance,NPMIMEType aType,NPStream * aStream,NPBool aSeekable,uint16_t * aStype)370 PluginAsyncSurrogate::NPP_NewStream(NPP aInstance, NPMIMEType aType,
371                                     NPStream* aStream, NPBool aSeekable,
372                                     uint16_t* aStype)
373 {
374   PluginAsyncSurrogate* surrogate = Cast(aInstance);
375   MOZ_ASSERT(surrogate);
376   return surrogate->NPP_NewStream(aType, aStream, aSeekable, aStype);
377 }
378 
379 /* static */ NPError
NPP_SetWindow(NPP aInstance,NPWindow * aWindow)380 PluginAsyncSurrogate::NPP_SetWindow(NPP aInstance, NPWindow* aWindow)
381 {
382   PluginAsyncSurrogate* surrogate = Cast(aInstance);
383   MOZ_ASSERT(surrogate);
384   return surrogate->NPP_SetWindow(aWindow);
385 }
386 
387 /* static */ void
NPP_Print(NPP aInstance,NPPrint * aPrintInfo)388 PluginAsyncSurrogate::NPP_Print(NPP aInstance, NPPrint* aPrintInfo)
389 {
390   PluginAsyncSurrogate* surrogate = Cast(aInstance);
391   MOZ_ASSERT(surrogate);
392   surrogate->NPP_Print(aPrintInfo);
393 }
394 
395 /* static */ int16_t
NPP_HandleEvent(NPP aInstance,void * aEvent)396 PluginAsyncSurrogate::NPP_HandleEvent(NPP aInstance, void* aEvent)
397 {
398   PluginAsyncSurrogate* surrogate = Cast(aInstance);
399   MOZ_ASSERT(surrogate);
400   return surrogate->NPP_HandleEvent(aEvent);
401 }
402 
403 /* static */ int32_t
NPP_WriteReady(NPP aInstance,NPStream * aStream)404 PluginAsyncSurrogate::NPP_WriteReady(NPP aInstance, NPStream* aStream)
405 {
406   PluginAsyncSurrogate* surrogate = Cast(aInstance);
407   MOZ_ASSERT(surrogate);
408   return surrogate->NPP_WriteReady(aStream);
409 }
410 
411 /* static */ NPError
NPP_DestroyStream(NPP aInstance,NPStream * aStream,NPReason aReason)412 PluginAsyncSurrogate::NPP_DestroyStream(NPP aInstance,
413                                         NPStream* aStream,
414                                         NPReason aReason)
415 {
416   PluginAsyncSurrogate* surrogate = Cast(aInstance);
417   MOZ_ASSERT(surrogate);
418   return surrogate->NPP_DestroyStream(aStream, aReason);
419 }
420 
PendingNewStreamCall(NPMIMEType aType,NPStream * aStream,NPBool aSeekable)421 PluginAsyncSurrogate::PendingNewStreamCall::PendingNewStreamCall(
422     NPMIMEType aType, NPStream* aStream, NPBool aSeekable)
423   : mType(NullableString(aType))
424   , mStream(aStream)
425   , mSeekable(aSeekable)
426 {
427 }
428 
429 /* static */ nsNPAPIPluginStreamListener*
GetStreamListener(NPStream * aStream)430 PluginAsyncSurrogate::GetStreamListener(NPStream* aStream)
431 {
432   nsNPAPIStreamWrapper* wrapper =
433     reinterpret_cast<nsNPAPIStreamWrapper*>(aStream->ndata);
434   if (!wrapper) {
435     return nullptr;
436   }
437   return wrapper->GetStreamListener();
438 }
439 
440 void
DestroyAsyncStream(NPStream * aStream)441 PluginAsyncSurrogate::DestroyAsyncStream(NPStream* aStream)
442 {
443   MOZ_ASSERT(aStream);
444   nsNPAPIPluginStreamListener* streamListener = GetStreamListener(aStream);
445   MOZ_ASSERT(streamListener);
446   // streamListener was suspended during async init. We must resume the stream
447   // request prior to calling _destroystream for cleanup to work correctly.
448   streamListener->ResumeRequest();
449   if (!mInstance) {
450     return;
451   }
452   parent::_destroystream(GetNPP(), aStream, NPRES_DONE);
453 }
454 
455 /* static */ bool
SetStreamType(NPStream * aStream,uint16_t aStreamType)456 PluginAsyncSurrogate::SetStreamType(NPStream* aStream, uint16_t aStreamType)
457 {
458   nsNPAPIPluginStreamListener* streamListener = GetStreamListener(aStream);
459   if (!streamListener) {
460     return false;
461   }
462   return streamListener->SetStreamType(aStreamType);
463 }
464 
465 void
OnInstanceCreated(PluginInstanceParent * aInstance)466 PluginAsyncSurrogate::OnInstanceCreated(PluginInstanceParent* aInstance)
467 {
468   if (!mDestroyPending) {
469     // If NPP_Destroy has already been called then these streams have already
470     // been cleaned up on the browser side and are no longer valid.
471     for (uint32_t i = 0, len = mPendingNewStreamCalls.Length(); i < len; ++i) {
472       PendingNewStreamCall& curPendingCall = mPendingNewStreamCalls[i];
473       uint16_t streamType = NP_NORMAL;
474       NPError curError = aInstance->NPP_NewStream(
475                       const_cast<char*>(NullableStringGet(curPendingCall.mType)),
476                       curPendingCall.mStream, curPendingCall.mSeekable,
477                       &streamType);
478       if (curError != NPERR_NO_ERROR) {
479         // If we failed here then the send failed and we need to clean up
480         DestroyAsyncStream(curPendingCall.mStream);
481       }
482     }
483   }
484   mPendingNewStreamCalls.Clear();
485   mInstantiated = true;
486 }
487 
488 /**
489  * During asynchronous initialization it might be necessary to wait for the
490  * plugin to complete its initialization. This typically occurs when the result
491  * of a plugin call depends on the plugin being fully instantiated. For example,
492  * if some JS calls into the plugin, the call must be executed synchronously to
493  * preserve correctness.
494  *
495  * This function works by pumping the plugin's IPC channel for events until
496  * initialization has completed.
497  */
498 bool
WaitForInit()499 PluginAsyncSurrogate::WaitForInit()
500 {
501   if (mInitCancelled) {
502     return false;
503   }
504   if (mAcceptCalls) {
505     return true;
506   }
507   Telemetry::AutoTimer<Telemetry::BLOCKED_ON_PLUGINASYNCSURROGATE_WAITFORINIT_MS>
508     timer(mParent->GetHistogramKey());
509   bool result = false;
510   MOZ_ASSERT(mParent);
511   if (mParent->IsChrome()) {
512     PluginProcessParent* process = static_cast<PluginModuleChromeParent*>(mParent)->Process();
513     MOZ_ASSERT(process);
514     process->SetCallRunnableImmediately(true);
515     if (!process->WaitUntilConnected()) {
516       return false;
517     }
518   }
519   if (!mParent->WaitForIPCConnection()) {
520     return false;
521   }
522   if (!mParent->IsChrome()) {
523     // For e10s content processes, we need to spin the content channel until the
524     // protocol bridging has occurred.
525     dom::ContentChild* cp = dom::ContentChild::GetSingleton();
526     mozilla::ipc::MessageChannel* contentChannel = cp->GetIPCChannel();
527     MOZ_ASSERT(contentChannel);
528     while (!mParent->mNPInitialized) {
529       if (mParent->mShutdown) {
530         // Since we are pumping the message channel for events, it may be
531         // possible for module initialization to fail during this loop. We must
532         // return false if this happens or else we'll be permanently stuck.
533         return false;
534       }
535       result = contentChannel->WaitForIncomingMessage();
536       if (!result) {
537         return result;
538       }
539     }
540   }
541   mozilla::ipc::MessageChannel* channel = mParent->GetIPCChannel();
542   MOZ_ASSERT(channel);
543   while (!mAcceptCalls) {
544     if (mInitCancelled) {
545       // Since we are pumping the message channel for events, it may be
546       // possible for plugin instantiation to fail during this loop. We must
547       // return false if this happens or else we'll be permanently stuck.
548       return false;
549     }
550     result = channel->WaitForIncomingMessage();
551     if (!result) {
552       break;
553     }
554   }
555   return result;
556 }
557 
558 void
AsyncCallDeparting()559 PluginAsyncSurrogate::AsyncCallDeparting()
560 {
561   ++mAsyncCallsInFlight;
562   if (!mPluginDestructionGuard) {
563     mPluginDestructionGuard = MakeUnique<PluginDestructionGuard>(this);
564   }
565 }
566 
567 void
AsyncCallArriving()568 PluginAsyncSurrogate::AsyncCallArriving()
569 {
570   MOZ_ASSERT(mAsyncCallsInFlight > 0);
571   if (--mAsyncCallsInFlight == 0) {
572     mPluginDestructionGuard.reset(nullptr);
573   }
574 }
575 
576 void
NotifyAsyncInitFailed()577 PluginAsyncSurrogate::NotifyAsyncInitFailed()
578 {
579   if (!mDestroyPending) {
580     // Clean up any pending NewStream requests
581     for (uint32_t i = 0, len = mPendingNewStreamCalls.Length(); i < len; ++i) {
582       PendingNewStreamCall& curPendingCall = mPendingNewStreamCalls[i];
583       DestroyAsyncStream(curPendingCall.mStream);
584     }
585   }
586   mPendingNewStreamCalls.Clear();
587 
588   // Make sure that any WaitForInit calls on this surrogate will fail, or else
589   // we'll be perma-blocked
590   mInitCancelled = true;
591 
592   if (!mInstance) {
593       return;
594   }
595   nsPluginInstanceOwner* owner = mInstance->GetOwner();
596   if (owner) {
597     owner->NotifyHostAsyncInitFailed();
598   }
599 }
600 
601 // static
602 NPObject*
ScriptableAllocate(NPP aInstance,NPClass * aClass)603 PluginAsyncSurrogate::ScriptableAllocate(NPP aInstance, NPClass* aClass)
604 {
605   PLUGIN_LOG_DEBUG_FUNCTION;
606   if (aClass != GetClass()) {
607     NS_ERROR("Huh?! Wrong class!");
608     return nullptr;
609   }
610 
611   return new AsyncNPObject(Cast(aInstance));
612 }
613 
614 // static
615 void
ScriptableInvalidate(NPObject * aObject)616 PluginAsyncSurrogate::ScriptableInvalidate(NPObject* aObject)
617 {
618   PLUGIN_LOG_DEBUG_FUNCTION;
619   if (aObject->_class != GetClass()) {
620     NS_ERROR("Don't know what kind of object this is!");
621     return;
622   }
623 
624   AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
625   if (!object->mSurrogate->WaitForInit()) {
626     return;
627   }
628   NPObject* realObject = object->GetRealObject();
629   if (!realObject) {
630     return;
631   }
632   realObject->_class->invalidate(realObject);
633 }
634 
635 // static
636 void
ScriptableDeallocate(NPObject * aObject)637 PluginAsyncSurrogate::ScriptableDeallocate(NPObject* aObject)
638 {
639   PLUGIN_LOG_DEBUG_FUNCTION;
640   if (aObject->_class != GetClass()) {
641     NS_ERROR("Don't know what kind of object this is!");
642     return;
643   }
644 
645   AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
646   delete object;
647 }
648 
649 // static
650 bool
ScriptableHasMethod(NPObject * aObject,NPIdentifier aName)651 PluginAsyncSurrogate::ScriptableHasMethod(NPObject* aObject,
652                                                   NPIdentifier aName)
653 {
654   PLUGIN_LOG_DEBUG_FUNCTION;
655   if (aObject->_class != GetClass()) {
656     NS_ERROR("Don't know what kind of object this is!");
657     return false;
658   }
659 
660   RecursionGuard guard;
661   if (guard.IsRecursive()) {
662     return false;
663   }
664 
665   AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
666   MOZ_ASSERT(object);
667   bool checkPluginObject = !object->mSurrogate->mInstantiated &&
668                            !object->mSurrogate->mAcceptCalls;
669 
670   if (!object->mSurrogate->WaitForInit()) {
671     return false;
672   }
673   NPObject* realObject = object->GetRealObject();
674   if (!realObject) {
675     return false;
676   }
677   bool result = realObject->_class->hasMethod(realObject, aName);
678   if (!result && checkPluginObject) {
679     // We may be calling into this object because properties in the WebIDL
680     // object hadn't been set yet. Now that we're further along in
681     // initialization, we should try again.
682     const NPNetscapeFuncs* npn = object->mSurrogate->mParent->GetNetscapeFuncs();
683     NPObject* pluginObject = nullptr;
684     NPError nperror = npn->getvalue(object->mSurrogate->GetNPP(),
685                                     NPNVPluginElementNPObject,
686                                     (void*)&pluginObject);
687     if (nperror == NPERR_NO_ERROR) {
688       NPPAutoPusher nppPusher(object->mSurrogate->GetNPP());
689       result = pluginObject->_class->hasMethod(pluginObject, aName);
690       npn->releaseobject(pluginObject);
691       NPUTF8* idstr = npn->utf8fromidentifier(aName);
692       npn->memfree(idstr);
693     }
694   }
695   return result;
696 }
697 
698 bool
GetPropertyHelper(NPObject * aObject,NPIdentifier aName,bool * aHasProperty,bool * aHasMethod,NPVariant * aResult)699 PluginAsyncSurrogate::GetPropertyHelper(NPObject* aObject, NPIdentifier aName,
700                                         bool* aHasProperty, bool* aHasMethod,
701                                         NPVariant* aResult)
702 {
703   PLUGIN_LOG_DEBUG_FUNCTION;
704 
705   if (!aObject) {
706     return false;
707   }
708 
709   RecursionGuard guard;
710   if (guard.IsRecursive()) {
711     return false;
712   }
713 
714   if (!WaitForInit()) {
715     return false;
716   }
717 
718   AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
719   NPObject* realObject = object->GetRealObject();
720   if (!realObject) {
721     return false;
722   }
723   if (realObject->_class != PluginScriptableObjectParent::GetClass()) {
724     NS_ERROR("Don't know what kind of object this is!");
725     return false;
726   }
727 
728   PluginScriptableObjectParent* actor =
729     static_cast<ParentNPObject*>(realObject)->parent;
730   if (!actor) {
731     return false;
732   }
733   bool success = actor->GetPropertyHelper(aName, aHasProperty, aHasMethod, aResult);
734   if (!success) {
735     const NPNetscapeFuncs* npn = mParent->GetNetscapeFuncs();
736     NPObject* pluginObject = nullptr;
737     NPError nperror = npn->getvalue(GetNPP(), NPNVPluginElementNPObject,
738                                     (void*)&pluginObject);
739     if (nperror == NPERR_NO_ERROR) {
740       NPPAutoPusher nppPusher(GetNPP());
741       bool hasProperty = nsJSObjWrapper::HasOwnProperty(pluginObject, aName);
742       NPUTF8* idstr = npn->utf8fromidentifier(aName);
743       npn->memfree(idstr);
744       bool hasMethod = false;
745       if (hasProperty) {
746         hasMethod = pluginObject->_class->hasMethod(pluginObject, aName);
747         success = pluginObject->_class->getProperty(pluginObject, aName, aResult);
748         idstr = npn->utf8fromidentifier(aName);
749         npn->memfree(idstr);
750       }
751       *aHasProperty = hasProperty;
752       *aHasMethod = hasMethod;
753       npn->releaseobject(pluginObject);
754     }
755   }
756   return success;
757 }
758 
759 // static
760 bool
ScriptableInvoke(NPObject * aObject,NPIdentifier aName,const NPVariant * aArgs,uint32_t aArgCount,NPVariant * aResult)761 PluginAsyncSurrogate::ScriptableInvoke(NPObject* aObject,
762                                                NPIdentifier aName,
763                                                const NPVariant* aArgs,
764                                                uint32_t aArgCount,
765                                                NPVariant* aResult)
766 {
767   PLUGIN_LOG_DEBUG_FUNCTION;
768   if (aObject->_class != GetClass()) {
769     NS_ERROR("Don't know what kind of object this is!");
770     return false;
771   }
772 
773   AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
774   if (!object->mSurrogate->WaitForInit()) {
775     return false;
776   }
777   NPObject* realObject = object->GetRealObject();
778   if (!realObject) {
779     return false;
780   }
781   return realObject->_class->invoke(realObject, aName, aArgs, aArgCount, aResult);
782 }
783 
784 // static
785 bool
ScriptableInvokeDefault(NPObject * aObject,const NPVariant * aArgs,uint32_t aArgCount,NPVariant * aResult)786 PluginAsyncSurrogate::ScriptableInvokeDefault(NPObject* aObject,
787                                                       const NPVariant* aArgs,
788                                                       uint32_t aArgCount,
789                                                       NPVariant* aResult)
790 {
791   PLUGIN_LOG_DEBUG_FUNCTION;
792   if (aObject->_class != GetClass()) {
793     NS_ERROR("Don't know what kind of object this is!");
794     return false;
795   }
796 
797   AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
798   if (!object->mSurrogate->WaitForInit()) {
799     return false;
800   }
801   NPObject* realObject = object->GetRealObject();
802   if (!realObject) {
803     return false;
804   }
805   return realObject->_class->invokeDefault(realObject, aArgs, aArgCount, aResult);
806 }
807 
808 // static
809 bool
ScriptableHasProperty(NPObject * aObject,NPIdentifier aName)810 PluginAsyncSurrogate::ScriptableHasProperty(NPObject* aObject,
811                                                     NPIdentifier aName)
812 {
813   PLUGIN_LOG_DEBUG_FUNCTION;
814   if (aObject->_class != GetClass()) {
815     NS_ERROR("Don't know what kind of object this is!");
816     return false;
817   }
818 
819   RecursionGuard guard;
820   if (guard.IsRecursive()) {
821     return false;
822   }
823 
824   AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
825   MOZ_ASSERT(object);
826   bool checkPluginObject = !object->mSurrogate->mInstantiated &&
827                            !object->mSurrogate->mAcceptCalls;
828 
829   if (!object->mSurrogate->WaitForInit()) {
830     return false;
831   }
832   NPObject* realObject = object->GetRealObject();
833   if (!realObject) {
834     return false;
835   }
836   bool result = realObject->_class->hasProperty(realObject, aName);
837   const NPNetscapeFuncs* npn = object->mSurrogate->mParent->GetNetscapeFuncs();
838   NPUTF8* idstr = npn->utf8fromidentifier(aName);
839   npn->memfree(idstr);
840   if (!result && checkPluginObject) {
841     // We may be calling into this object because properties in the WebIDL
842     // object hadn't been set yet. Now that we're further along in
843     // initialization, we should try again.
844     NPObject* pluginObject = nullptr;
845     NPError nperror = npn->getvalue(object->mSurrogate->GetNPP(),
846                                     NPNVPluginElementNPObject,
847                                     (void*)&pluginObject);
848     if (nperror == NPERR_NO_ERROR) {
849       NPPAutoPusher nppPusher(object->mSurrogate->GetNPP());
850       result = nsJSObjWrapper::HasOwnProperty(pluginObject, aName);
851       npn->releaseobject(pluginObject);
852       idstr = npn->utf8fromidentifier(aName);
853       npn->memfree(idstr);
854     }
855   }
856   return result;
857 }
858 
859 // static
860 bool
ScriptableGetProperty(NPObject * aObject,NPIdentifier aName,NPVariant * aResult)861 PluginAsyncSurrogate::ScriptableGetProperty(NPObject* aObject,
862                                                     NPIdentifier aName,
863                                                     NPVariant* aResult)
864 {
865   PLUGIN_LOG_DEBUG_FUNCTION;
866   // See GetPropertyHelper below.
867   NS_NOTREACHED("Shouldn't ever call this directly!");
868   return false;
869 }
870 
871 // static
872 bool
ScriptableSetProperty(NPObject * aObject,NPIdentifier aName,const NPVariant * aValue)873 PluginAsyncSurrogate::ScriptableSetProperty(NPObject* aObject,
874                                                     NPIdentifier aName,
875                                                     const NPVariant* aValue)
876 {
877   PLUGIN_LOG_DEBUG_FUNCTION;
878   if (aObject->_class != GetClass()) {
879     NS_ERROR("Don't know what kind of object this is!");
880     return false;
881   }
882 
883   AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
884   if (!object->mSurrogate->WaitForInit()) {
885     return false;
886   }
887   NPObject* realObject = object->GetRealObject();
888   if (!realObject) {
889     return false;
890   }
891   return realObject->_class->setProperty(realObject, aName, aValue);
892 }
893 
894 // static
895 bool
ScriptableRemoveProperty(NPObject * aObject,NPIdentifier aName)896 PluginAsyncSurrogate::ScriptableRemoveProperty(NPObject* aObject,
897                                                        NPIdentifier aName)
898 {
899   PLUGIN_LOG_DEBUG_FUNCTION;
900   if (aObject->_class != GetClass()) {
901     NS_ERROR("Don't know what kind of object this is!");
902     return false;
903   }
904 
905   AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
906   if (!object->mSurrogate->WaitForInit()) {
907     return false;
908   }
909   NPObject* realObject = object->GetRealObject();
910   if (!realObject) {
911     return false;
912   }
913   return realObject->_class->removeProperty(realObject, aName);
914 }
915 
916 // static
917 bool
ScriptableEnumerate(NPObject * aObject,NPIdentifier ** aIdentifiers,uint32_t * aCount)918 PluginAsyncSurrogate::ScriptableEnumerate(NPObject* aObject,
919                                                   NPIdentifier** aIdentifiers,
920                                                   uint32_t* aCount)
921 {
922   PLUGIN_LOG_DEBUG_FUNCTION;
923   if (aObject->_class != GetClass()) {
924     NS_ERROR("Don't know what kind of object this is!");
925     return false;
926   }
927 
928   AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
929   if (!object->mSurrogate->WaitForInit()) {
930     return false;
931   }
932   NPObject* realObject = object->GetRealObject();
933   if (!realObject) {
934     return false;
935   }
936   return realObject->_class->enumerate(realObject, aIdentifiers, aCount);
937 }
938 
939 // static
940 bool
ScriptableConstruct(NPObject * aObject,const NPVariant * aArgs,uint32_t aArgCount,NPVariant * aResult)941 PluginAsyncSurrogate::ScriptableConstruct(NPObject* aObject,
942                                                   const NPVariant* aArgs,
943                                                   uint32_t aArgCount,
944                                                   NPVariant* aResult)
945 {
946   PLUGIN_LOG_DEBUG_FUNCTION;
947   if (aObject->_class != GetClass()) {
948     NS_ERROR("Don't know what kind of object this is!");
949     return false;
950   }
951 
952   AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
953   if (!object->mSurrogate->WaitForInit()) {
954     return false;
955   }
956   NPObject* realObject = object->GetRealObject();
957   if (!realObject) {
958     return false;
959   }
960   return realObject->_class->construct(realObject, aArgs, aArgCount, aResult);
961 }
962 
963 const NPClass PluginAsyncSurrogate::sNPClass = {
964   NP_CLASS_STRUCT_VERSION,
965   PluginAsyncSurrogate::ScriptableAllocate,
966   PluginAsyncSurrogate::ScriptableDeallocate,
967   PluginAsyncSurrogate::ScriptableInvalidate,
968   PluginAsyncSurrogate::ScriptableHasMethod,
969   PluginAsyncSurrogate::ScriptableInvoke,
970   PluginAsyncSurrogate::ScriptableInvokeDefault,
971   PluginAsyncSurrogate::ScriptableHasProperty,
972   PluginAsyncSurrogate::ScriptableGetProperty,
973   PluginAsyncSurrogate::ScriptableSetProperty,
974   PluginAsyncSurrogate::ScriptableRemoveProperty,
975   PluginAsyncSurrogate::ScriptableEnumerate,
976   PluginAsyncSurrogate::ScriptableConstruct
977 };
978 
PushSurrogateAcceptCalls(PluginInstanceParent * aInstance)979 PushSurrogateAcceptCalls::PushSurrogateAcceptCalls(PluginInstanceParent* aInstance)
980   : mSurrogate(nullptr)
981   , mPrevAcceptCallsState(false)
982 {
983   MOZ_ASSERT(aInstance);
984   mSurrogate = aInstance->GetAsyncSurrogate();
985   if (mSurrogate) {
986     mPrevAcceptCallsState = mSurrogate->SetAcceptingCalls(true);
987   }
988 }
989 
~PushSurrogateAcceptCalls()990 PushSurrogateAcceptCalls::~PushSurrogateAcceptCalls()
991 {
992   if (mSurrogate) {
993     mSurrogate->SetAcceptingCalls(mPrevAcceptCallsState);
994   }
995 }
996 
997 } // namespace plugins
998 } // namespace mozilla
999