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