1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
|
---
title: IPDL Tutorial
slug: IPDL/Tutorial
translation_of: Mozilla/IPDL/Tutorial
---
<p>IPDL, raccourci pour "Inter-process-communication Protocol Definition Language (Langage de Définition de Protocole de communication-intra-processus)", est un langage spécifique à Mozilla permettent le codage en C++ afin de transmettre des messages entre les processus ou mappes de façon organisée et sécurisée. Tous messages destnés aux implants(plugins) et onglets multi-processus de Firefox sont programmés en langage IPDL.</p>
<div class="note">Pour experimenter l'ajout d'un nouveau protocole IPDL, voir <a href="/en-US/docs/IPDL/Creating_a_New_Protocol" title="en-US/docs/IPDL/Creating a New Protocol"><span>Creation d'un Nouveau Protocole</span></a>.</div>
<p>Tous les messages IPDL sont envoyés entre des polarités <strong>parents/enfants</strong>, appelés <strong>acteurs</strong>. Un <strong>protocole</strong> IPDL programme la façon dont les acteurs communiquent: il progrmme les éventuels <strong>messages</strong> pouvant être envoyés entre les acteurs, ainsi qu'une <strong>machine officielle</strong> indiquant quand des messages peuvent être envoyés.</p>
<p>L'acteur parent est typiquement le côté le plus permanent de la conversation:</p>
<table style="">
<caption>Acteurs Parents/Enfants</caption>
<thead>
<tr>
<th scope="col"> </th>
<th scope="col">Parent</th>
<th scope="col">Enfant</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Onglets IPC</th>
<td>processus Chrome</td>
<td>processus Contenu</td>
</tr>
<tr>
<th scope="row">Implants/Plugins IPC</th>
<td>processus Contenu</td>
<td>Processus Implaznt/Plugin</td>
</tr>
</tbody>
</table>
<p>Chaque protocole est programmé dans un fichier séparé. Le compilateur IPDL génère plusieurs en-têtes C++ avec chaque protocole IPDL. Le code généré organise les détails de la couche initiale de communication (prises et conduits), construisant et envoyant les messages, assurant que tous les acteurs respectent leurs particularités, et contrôlant de nombreux cas d'erreur. Le code IPDL suivant definit une interaction très basique dui navigateur et des implants/plugin acteurs:</p>
<pre><strong>async protocol</strong> PPlugin
{
<strong>child:</strong>
Init(nsCString pluginPath);
Shutdown();
<strong>parent:</strong>
Ready();
};
</pre>
<p>Ce code programme le protocole <code>PPlugin</code>. Deux messages peuvent être envoyés du parent à l'enfant, <code>"Init(Initialisation)"</code> et <code>"Shutdown(Fermeture)"</code>. Un message peut être envoyé de l'enfant au parent, <code>Ready(Prêt)</code>.</p>
<div class="note">Les protocoles IPDL commencent avec la lettre P. Le fichier dans lequel le protocole est programmé doit avoir un nom concnordant, PPlugin.ipdl.</div>
<h3 id="Code_C_Generé"><span class="mw-headline">Code C++ Generé</span></h3>
<p>Lorsque PPlugin.ipdl est compilé, les en-têtes <code>PPluginParent.h</code>, et <code>PPluginChild.h</code> seront generés dans le répertoire ipc/ipdl/_ipdlheaders/ de l'arbre ainsi construit. Les classes PPluginParent et PPluginChild sont des classes abstraites qui doivent être sous-classées. Chaque message sortant est une méthode C++ qui peut être appelée. Chaque message entrant est une méthode C++ purement virtuelle qui doit être mise en oeuvre:</p>
<pre>class PPluginParent
{
public:
bool SendInit(const nsCString& pluginPath) {
// generated code to send an Init() message
}
bool SendShutdown() {
// generated code to send a Shutdown() message
}
protected:
/**
* A subclass of PPluginParent must implement this method to handle the Ready() message.
*/
bool RecvReady() = 0;
};
class PPluginChild
{
protected:
bool RecvInit(const nsCString& pluginPath) = 0;
bool RecvShutdown() = 0;
public:
bool SendReady() {
// generated code to send a Ready() message
}
};
</pre>
<p>Ces classes abstraites Parent et Child assurent tous les problèmes de "couche de protocole": tri du data, envoyant et recevant les messages, et vérifiant la sécurité des protocoles. C'est la responsabilité de l'exécuteur de créer des sous-classes afin de réaliser le travail du moment inclus dans chaque message. Voici un exemple-brouillon de comment un développeur de navigateur pourrait utiliser PPluginParent.</p>
<pre>class PluginParent : public PPluginParent
{
public:
PluginParent(const nsCString& pluginPath) {
// launch child plugin process
SendInit(pluginPath);
}
~PluginParent() {
SendShutdown();
}
protected:
bool RecvReady() {
mObservers.Notify("ready for action");
}
};
</pre>
<p>Voici comment le "PPluginChild" pourrait être utilisé par un développeur C++ dans le processus plugin:</p>
<pre>class PluginChild : public PPluginChild
{
protected:
void RecvInit(const nsCString& pluginPath) {
mPluginLibrary = PR_LoadLibrary(pluginPath.get());
SendReady();
}
void RecvShutdown() {
PR_UnloadLibrary(mPluginLibrary);
}
private:
PRLibrary* mPluginLibrary;
};
</pre>
<p>Démarrer le sous-processus et installer ces acteurs de protocole dans notre "couche de transport" IPC n'est pas le but de ce document. Voir <a href="/en-US/docs/IPDL/Processes_and_Threads" title="en-US/docs/IPDL/Processes and Threads">Processus et Connections IPDL</a> pour plus de détails.</p>
<p>Vu que les messages de protocole sont representées comme des méthodes C++, il est facile d'oublier qu'ils sont en fait des messages asynchrones: par defaut, la méthode C++ se commutera immediatement, avant que les message soient rendus à destination.</p>
<p>Les parametres des méthodes Recv* (<em>const nsCString& pluginPath</em> dans l'exemple) font référence à des objets temporaires; aussi, il vaut mieux les copier si vous avez besoin de conserver leurs datas.</p>
<h3 id="Direction"><span class="mw-headline">Direction </span></h3>
<p>Chaque type de message inclut une "direction." La direction du message indique si le message peut être envoyé de-parent-à-enfant, d'-enfant-à-parent, ou dans les deux sens. Trois mots-clefs servent d'indicateurs de direction; <strong>l'enfant</strong> a été introduit ci-dessus. Le second est le <strong>parent</strong>, ce qui veut dire que les messages declarés en tant que <strong>parent</strong> ne peuvent être qu'envoyés d'-enfant-à-parent. Le troisième est <strong>les deux</strong>, ce qui veut dire que les messages declarés peuvent être envoyés dans les deux directions. L'exemple artificiel suivant montre comment ces particularités sont utilisées et comment ces particularités changent les classes acteurs abstaites ainsi generées.</p>
<pre>// PDirection.ipdl
<strong>async protocol</strong> PDirection
{
<strong>child:</strong>
Foo(); // can be sent from-parent-to-child
<strong>parent:</strong>
Bar(); // can be sent from-child-to-parent
<strong>both:</strong>
Baz(); // can be sent both ways
};
</pre>
<pre>// PDirectionParent.h
class PDirectionParent
{
protected:
virtual void RecvBar() = 0;
virtual void RecvBaz() = 0;
public:
void SendFoo() { /* boilerplate */ }
void SendBaz() { /* boilerplate */ }
};
</pre>
<pre>// PDirectionChild.h
class PDirectionChild
{
protected:
virtual void RecvFoo() = 0;
virtual void RecvBaz() = 0;
public:
void SendBar() { /* boilerplate */ }
void SendBaz() { /* boilerplate */ }
};
</pre>
<p><code>Vous pouvez utiliser l'enfant</code>, le <code>parent</code>, <code>et les deux <span style="font-family: Verdana,Tahoma,sans-serif;">labels </span></code>de multiples fois dans un protocole de réalisation. Ils se comportent tels des labels <code>publiques</code>, <code>protégés</code>, et <code>privés dans</code> C++.</p>
<h3 id="Paramètres"><span class="mw-headline">Paramètres </span></h3>
<p>Les programations de messages permettent tous nombres of <strong>paramètres</strong>. Les Paramètres indiquent les données envoyées avec le message. Leurs valeurs sont codées par l'envoyeur et décodées par le receveur. IPDL authorise les caractères inclus et les personnalisations de base, de même que les traits d'union et les flèches.</p>
<div>
<div>
<div class="f">
<p>The built-in simple types include the C++ integer types (bool, char, int, double) and XPCOM string types (<code>nsString</code>, <code>nsCString</code>). IPDL imports these automatically because they are common, and because the base IPC library knows how to serialize and deserialize these types. See <code>ipc/ipdl/ipdl/builtin.py</code> for the most up-to-date list of automatically imported types.</p>
<p>Actors may be passed as parameters. The C++ signature will accept a PProtocolParent* on one side and convert it to a PProtocolChild* on the other.</p>
<p>Custom primitive types. When you need to send data of a type other than one built into IPDL, you can add a <code>using</code> declaration in an IPDL specification.<br>
A <a href="/en-US/docs/IPDL/Type_Serialization" title="en-US/docs/IPDL/Type Serialization">custom serializer and deserializer</a> must be provided by your C++ code.</p>
<pre><strong>using</strong> mozilla::plugins::NPRemoteEvent;
<strong>sync protocol</strong> PPluginInstance
{
<strong>child:</strong>
HandleEvent(NPRemoteEvent);
};
</pre>
<h4 id="Unions">Unions</h4>
<p>IPDL has built-in support for declaring discriminated unions.</p>
<pre><strong>using</strong> struct mozilla::void_t from "ipc/IPCMessageUtils.h";
<strong>union</strong> Variant
{
void_t;
bool;
int;
double;
nsCString;
PPluginScriptableObject;
};</pre>
<p>This union generates a C++ interface which includes the following:</p>
<pre><span style="font-weight: bold;">struct</span> Variant
{
enum Type {
Tvoid_t, Tbool, Tint, Tdouble, TnsCString, TPPlugionScriptableObject
};
Type type();
void_t& get_void_t();
bool& get_bool();
int& get_int();
double& get_double();
nsCString& get_nsCString();
PPluginScriptableObject* get_PPluginScriptableObject();
};
</pre>
<p>aUnion.type() can be used to determine the type of a union received in an IPDL message handler, with the remaining functions granting access to its contents. To initialize a union, simply assign a valid value to it, as follows:</p>
<pre>aVariant = false;
</pre>
<h4 id="Structs">Structs</h4>
<p>IPDL has built-in support for arbitrary collections of serializable data types.</p>
<pre><strong>struct</strong> NameValuePair
{
nsCString name;
nsCString value;
};</pre>
<p>In implementation code, these structs can be created and used like so:</p>
<pre>NameValuePair entry(aString, anotherString);
foo(entry.name(), entry.value()); // Named accessor functions return references to the members
</pre>
<h4 id="Arrays">Arrays</h4>
<p>IPDL has simple syntax for arrays:</p>
<pre>InvokeMethod(nsCString[] args);</pre>
<p> In C++ this is translated into a <a href="/en-US/docs/XPCOM_array_guide#nsTArray.3cT.3e" title="en-US/docs/XPCOM array guide#nsTArray.3cT.3e">nsTArray</a> reference:</p>
<pre>virtual bool RecvInvokeMethod(nsTArray<nsCString>& args);
</pre>
</div>
</div>
</div>
<p>IPDL's generated data structures can be used in several protocols if they are defined in a separate <em>.ipdlh</em> file. These files must be added to the <em>ipdl.mk</em> makefile like regular <em>.ipdl</em> files, and they use the same syntax (except they cannot declare protocols). To use the structures defined in <em>Foo.ipdlh</em>, include it as follows.</p>
<pre>// in a .ipdl file
<strong>include</strong> Foo;
</pre>
<h3 id="Synchronous_and_RPC_Messaging"><span class="mw-headline">Synchronous and RPC Messaging</span></h3>
<p>Up until now, all the messages have been <strong>asynchronous</strong>. The message is sent, and the C++ method returns immediately. But what if we wanted to wait until the message was handled, or get return values from a message?</p>
<p>In IPDL, there are three different semantics:</p>
<ol>
<li><strong>asynchronous</strong> semantics; the sender is not blocked.</li>
<li>Wait until the receiver acknowledges that it received the message. We call this <strong>synchronous</strong> semantics, as the sender blocks until the receiver receives the message and sends back a reply. The message may have return values.</li>
<li><strong>rpc</strong> semantics are a variation on synchronous semantics, see below.</li>
</ol>
<p>Note that the parent can send messages to the child, and vice versa, so 'sender' and 'receiver' in the above three cases can be either the parent or the child. The messaging semantics applies in the same way to both directions. So, for example, in synchronous semantics from the child to the parent, the child will block until the parent receives the message and the response arrives, and in <strong>a</strong>synchronous semantics from the parent to the child the parent will not block.</p>
<p>When creating a plugin instance, the browser should block until instance creation is finished, and needs some information returned from the plugin:</p>
<pre><strong>protocol</strong> PPluginInstance
{
<strong>child:</strong>
<strong>sync</strong> Init() <strong>returns</strong> (bool windowless, bool ok);
};
</pre>
<p>We added two new keywords to the Plugin protocol, <strong>sync</strong> and <strong>returns</strong>. <strong>sync</strong> marks a message as being sent synchronously (async is the default if no semantic is specified). The <strong>returns</strong> keyword marks the beginning of the list of values that are returned in the reply to the message. It is an error to add a <strong>returns</strong> block to an asynchronous message.</p>
<p>To make the blocking nature more noticeable to programmers, the C++ method names for synchronous and RPC messages are different:</p>
<table style="">
<thead>
<tr>
<th scope="col"> </th>
<th scope="col">sender</th>
<th scope="col">receiver</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">async/sync</th>
<td>Send<em>MessageName</em></td>
<td>Recv<em>MessageName</em></td>
</tr>
<tr>
<th scope="row">rpc</th>
<td>Call<em>MessageName</em></td>
<td>Answer<em>MessageName</em></td>
</tr>
</tbody>
</table>
<h4 id="Message_Semantics_Strength"><span class="mw-headline">Message Semantics Strength </span></h4>
<p>The above protocol will fail the IPDL type checker. IPDL protocols also have "semantics specifiers", just like messages. A protocol must be declared to have semantics at least as "strong" as its strongest message semantics. Synchronous semantics is called "stronger than" asynchronous. Like message declarations, the default protocol semantics is asynchronous; however, since the Plugin protocol declares a synchronous message, this type rule is violated. The fixed up Plugin protocol is shown below.</p>
<pre><strong>sync protocol</strong> PPluginInstance
{
<strong>child:
</strong><strong>sync</strong> Init() <strong>returns</strong> (bool windowless, bool ok);
};</pre>
<p>The generated C++ code for this method uses outparam pointers for the returned values:</p>
<pre>class PPluginInstanceParent
{
...
bool SendInit(bool* windowless, bool* ok) { ... };
};
class PPluginInstanceChild
{
...
virtual bool RecvInit(bool* windowless, bool* ok) = 0;
}</pre>
<h4 id="RPC_semantics"><span class="mw-headline">RPC semantics </span></h4>
<p>"RPC" stands for "remote procedure call," and this third semantics models procedure call semantics. A quick summary of the difference between RPC and sync semantics is that RPC allows "re-entrant" message handlers: while an actor is blocked waiting for an "answer" to an RPC "call", it can be <em>unblocked</em> to handle a new, incoming RPC <em>call</em>.</p>
<p>In the example protocol below, the child actor offers a "CallMeCallYou()" RPC interface, and the parent offers a "CallYou()" RPC interface. The <code><strong>rpc</strong></code> qualifiers mean that if the parent calls "CallMeCallYou()" on the child actor, then the child actor, while servicing this call, is allowed to call back into the parent actor's "CallYou()" message.</p>
<pre><strong>rpc</strong> <strong>protocol</strong> Example {
<strong>child</strong>:
<strong>rpc</strong> CallMeCallYou() <strong>returns</strong> (int rv);
<strong>parent</strong>:
<strong>rpc</strong> CallYou() <strong>returns</strong> (int rv);
};
</pre>
<p>If this were instead a sync protocol, the child actor would not be allowed to call the parent actor's "CallYou()" method while servicing the "CallMeCallYou()" message. (The child actor would be terminated with extreme prejudice.)</p>
<h4 id="Preferred_semantics"><span class="mw-headline">Preferred semantics </span></h4>
<p>Use <strong>async</strong> semantics whenever possible.</p>
<p>Blocking on replies to messages is discouraged. If you absolutely need to block on a reply, use <strong>sync</strong> semantics <em>very carefully</em>. It is possible to get into trouble with careless uses of synchronous messages; while IPDL can check and/or guarantee that your code does not deadlock, it is easy to cause nasty performance problems by blocking.</p>
<p>Please don't use RPC semantics. RPC semantics exists mainly to support remoting plugins (NPAPI), where we have no choice.</p>
<div class="note">Chrome to content calls (for IPC tabs) must only use async semantics. In order to preserve responsiveness, the chrome process may never block on a content process which may be busy or hung.</div>
<h3 id="Message_Delivery_Order">Message Delivery Order</h3>
<p>Delivery is "in-order", that is, messages are delivered to the receiver in the order that they are sent, regardless of the messages' semantics. If an actor A sends messages M1 then M2 to actor B, B will be awoken to process M1 <em>then</em> M2.</p>
<h2 id="Subprotocols_and_Protocol_Management"><span class="mw-headline">Subprotocols and Protocol Management </span></h2>
<p>So far we've seen a single protocol, but no real-world situation would have a single protocol in isolation. Instead, protocols are arranged in a managed hierarchy of <strong>subprotocols</strong>. A sub-protocol is bound to a "manager" which tracks its lifetime and acts as a factory. A protocol hierarchy begins with a single top-level protocol from which all subprotocol actors are eventually created. In Mozilla there are two main top-level protocols: <a class="external" href="http://mxr.mozilla.org/mozilla-central/source/dom/plugins/ipc/PPluginModule.ipdl" title="http://mxr.mozilla.org/projects-central/source/electrolysis/dom/plugins/PPluginModule.ipdl">PPluginModule</a> for remote plugins, and <a class="external" href="http://mxr.mozilla.org/mozilla-central/source/dom/ipc/PContent.ipdl" title="http://mxr.mozilla.org/mozilla-central/source/dom/ipc/PContent.ipdl">PContent</a> for remote tabs.</p>
<p>The following example extends the toplevel plugin protocol to manage plugin instances.</p>
<pre><code>// ----- file PPlugin.ipdl
<strong>include protocol</strong> PPluginInstance;</code>
<code><strong>rpc protocol</strong> PPlugin
{
<strong> manages</strong> PPluginInstance;
<strong>child:</strong>
<strong>rpc</strong> Init(nsCString pluginPath) <strong>returns</strong> (bool ok);
<span style="font-weight: bold;">rpc </span>PPluginInstance(nsCString type, nsCString[] args) <strong>returns</strong> (int rv);
</code><code>};</code>
</pre>
<pre><code>// ----- file PPluginInstance.ipdl
<strong>include protocol</strong> PPlugin;
<strong>rpc protocol</strong> PPluginInstance
{
<strong> manager</strong> PPlugin;
<strong>child:</strong>
<strong>rpc</strong> __delete__();
SetSize(int width, int height);
};</code></pre>
<p>This example has several new elements: `include protocol` imports another protocol declaration into this file. Note that this is not a preprocessor directive, but a part of the IPDL language. The generated C++ code will have proper #include preprocessor directives for the imported protocols.</p>
<p>The `manages` statement declares that this protocol manages PPluginInstance. The PPlugin protocol must declare constructor and destructor messages for PPluginInstance actors. The `manages` statement also means that PPluginInstance actors are tied to the lifetime of the Plugin actor that creates them: if this PPlugin instance is destroyed, all the PPluginInstances associated with it become invalid or are destroyed as well.</p>
<p>The mandatory constructor and destructor messages (PPluginInstance and __delete__ respectively) exist, confusingly, in separate locations. The constructor must be located in the managing protocol, while the destructor belongs to the managed subprotocol. These messages have syntax similar to C++ constructors, but the behavior is different. Constructors and destructors have parameters, direction, semantics, and return values like other IPDL messages. A constructor and destructor message must be declared for each managed protocol.</p>
<p>Each subprotocol must include a `manager` statement.</p>
<p>At the C++ layer, the subclasses in both the child and the parent must implement methods for allocating and deallocating the subprotocol actor. The constructor and destructor are translated into standard C++ methods for messages.</p>
<p>Note: __delete__ is a built-in construct, and is the only IPDL message which does not require an overridden implementation (ie. Recv/Answer__delete__). However, overridden implementations are encouraged when some action should happen on protocol destruction in lieu of using the DeallocPProtocol function.</p>
<pre>class PPluginParent
{
/* Allocate a PPluginInstanceParent when the first form of CallPluginInstanceConstructor is called */
virtual PPluginInstanceParent* AllocPPluginInstance(const nsCString& type, const nsTArray<nsCString>& args, int* rv) = 0;
/* Deallocate the PPluginInstanceParent after PPluginInstanceDestructor is done with it */
virtual bool DeallocPPluginInstance(PPluginInstanceParent* actor) = 0;
/* constructor message */
virtual CallPPluginInstanceConstructor(const nsCString& type, const nsTArray<nsCString>& args, int* rv) { /* generated code */ }
/* alternate form of constructor message: supply your own PPluginInstanceParent* to bypass AllocPPluginInstance */
virtual bool CallPPluginInstanceConstructor(PPluginInstanceParent* actor, const nsCString& type, const nsTArray<nsCString>& args, int* rv)
{ /* generated code */ }
/* destructor message */
virtual bool Call__delete__(PPluginInstanceParent* actor) { /* generated code */ }
/* Notification that actor deallocation is imminent, IPDL mechanisms are now unusable */
virtual void ActorDestroy(ActorDestroyReason why);
...
};
class PPluginChild
{
/* Allocate a PPluginInstanceChild when we receive the PPluginInstance constructor */
virtual PPluginInstanceChild* AllocPPluginInstance(const nsCString& type, const nsTArray<nsCString>& args, int* rv) = 0;
/* Deallocate a PPluginInstanceChild after we handle the PPluginInstance destructor */
virtual bool DeallocPPluginInstance(PPluginInstanceChild* actor) = 0;
/* Answer the constructor message. Implementing this method is optional: it may be possible to answer the message directly in AllocPPluginInstance. */
virtual bool AnswerPPluginInstanceConstructor(PPluginInstanceChild* actor, const nsCString& type, const nsTArray<nsCString>& args, int* rv) { }
/* Answer the desctructor message. */
virtual bool Answer__delete__(PPluginInstanceChild* actor) = 0;
/* Notification that actor deallocation is imminent, IPDL mechanisms are now unusable */
virtual void ActorDestroy(ActorDestroyReason why);
...
};</pre>
<h4 id="Subprotocol_Actor_Lifetime">Subprotocol Actor Lifetime</h4>
<p>AllocPProtocol and DeallocPProtocol are a matched pair of functions. The typical implementation of these functions uses `new` and `delete`:</p>
<pre>class PluginChild : PPluginChild
{
virtual PPluginInstanceChild* AllocPPluginInstance(const nsCString& type, const nsTArray<nsCString>& args, int* rv)
{
return new PluginInstanceChild(type, args, rv);
}
virtual bool DeallocPPluginInstanceChild(PPluginInstanceChild* actor)
{
delete actor; // actor destructors are always virtual, so it's safe to call delete on them!
return true;
}
...
};</pre>
<p>In some cases, however, external code may hold references to actor implementations which require refcounting or other lifetime strategies. In this case, the alloc/dealloc pairs can perform different actions. Here is an example of refcounting:</p>
<pre>class ExampleChild : public nsIObserver, public PExampleChild { ... };
virtual PExampleChild* TopLevelChild::AllocPExample()
{
nsRefPtr<ExampleChild*> actor = new ExampleChild();
return actor.forget();
}
virtual bool TopLevelChild::DeallocPExample(PExampleChild* actor)
{
NS_RELEASE(static_cast<ExampleChild*>(actor));
return true;
}
</pre>
<p>If an object that implements a protocol can't be constructed inside AllocPFoo, or has been previously constructed and doesn't require an IPDL connection throughout its lifetime, there is a second form of SendPFooConstructor which can be used:</p>
<pre>class ExampleChild
{
public:
void DoSomething() {
aManagerChild->SendPExampleConstructor(this, ...);
}
};
</pre>
<p>Internally, the first constructor form simply calls</p>
<pre>PExample(Parent|Child)* actor = AllocPExample(...);
SendPExampleConstructor(actor, ...);
return actor;
</pre>
<p>with the same effect.</p>
<h4 id="Subprotocol_Deletion">Subprotocol Deletion</h4>
<p>It is worth understanding the protocol deletion process. Given the simple protocols:</p>
<pre>// --- PExample.ipdl
<strong>include protocol</strong> PSubExample;
<strong>async protocol</strong> PExample
{
<strong>manages</strong> PSubExample;
<strong>p</strong><strong>arent:
</strong>PChild();
};
// --- PSubExample.ipdl
<strong>include protocol</strong> PExample;
<strong>async protocol</strong> PSubExample
{
<strong>manager </strong>PExample;
<strong>child:
</strong> __delete__();
};
</pre>
<p>We assume that there is a PSubExampleParent/Child pair in existence, such that some element now wishes to trigger the protocol's deletion from the parent side.</p>
<pre><code><code>aPSubExampleParent->Send__delete__();</code></code></pre>
<p>will trigger the following ordered function calls:</p>
<pre>PSubExampleParent::ActorDestroy(Deletion)
/* Deletion is an enumerated value indicating
that the destruction was intentional */
PExampleParent::DeallocPSubExample()</pre>
<pre>PSubExampleChild::Recv__delete__()
PSubExampleChild::ActorDestroy(Deletion)
PExampleChild::DeallocPSubExample()</pre>
<p>ActorDestroy is a generated function that allows code to run with the knowledge that actor deallocation is imminent. This is useful for actors with lifetimes outside of IPDL - for instance, a flag could be set indicating that IPDL-related functions are no longer safe to use.</p>
<h4 id="Accessing_the_protocol_tree_from_C">Accessing the protocol tree from C++</h4>
<p>The IPDL compiler generates methods that allow actors to access their manager (if the actor isn't top-level) and their managees (if any) from C++. For a protocol PFoo managed by PManager, that manages PManagee, the methods are</p>
<pre>PManager* PFoo::Manager()
const InfallibleTArray<PManagee*> PFoo::ManagedPManagee();
void PFoo::ManagedPManagee(InfallibleTArray<PManagee*>&);
</pre>
<h2 id="Shutdown_and_Error_Handling">Shutdown and Error Handling</h2>
<p>The C++ methods which implement IPDL messages return <code>bool</code>: true for success, and false for catastrophic failure. Message implementations should return false from a message implementation if the data is corrupted or otherwise malformed. Any time a message implementation returns false, IPDL will immediately begin catastrophic error handling: the communication channels for the child process (tab or plugin) will be disconnected, and the process will be terminated. Do not return false from message handlers for "normal" error conditions such as inability to load a network request! Normal errors should be signaled with a message or return value.</p>
<p><em>Note: the following paragraphs are not yet implemented.</em> IPDL tracks all active protocols between two endpoints. If if the child side crashes or becomes hung:</p>
<ul>
<li>any synchronous or RPC messages currently active will return false</li>
<li>no further messages will be accepted (C++ methods will return false)</li>
<li>each IPDL actor will receive an OnError message</li>
<li>DeallocPSubprotocol will be called on each manager protocol to deallocate any active subprotocols.</li>
</ul>
<p>When a manager protocol is destroyed, any subprotocols will be notified:</p>
<ul>
<li>no further messages will be accepted</li>
<li>DeallocPSubprotocol will be called on the manager protocol to deallocate any active subprotocols</li>
</ul>
<p>When the toplevel protocol is destroyed, this is equivalent to shutting down the entire IPDL machinery for that connection, because no more messages can be sent and all subprotocols are destroyed.</p>
<h2 id="Protocol_state_machines"><span class="mw-headline">Protocol state machines </span></h2>
<p>The astute reader might question why IPDL includes the word "protocol" when all that has been introduced so far are unstructured grab-bags of messages. IPDL allows protocol authors to define the order and structure of how messages may be sent/received by defining protocol <em>state machines</em> (finite state machines).</p>
<p>[Note that the state machine portion of the IPDL compiler is not complete as of this writing, 22 October 2009. IPDL code for state machines is accepted by the compiler, but it does not affect the generated C++, yet.]</p>
<p>IPDL parent and child actors communicating via a protocol are paired. Each actor in the pair follows the same state machine. The pair attempts to keep their single collective state synchronized. Though, it is possible that the parent and child actors may be momentarily out of sync while messages are transmitted.</p>
<p>IPDL (arbitrarily) requires state machines to be defined from the perspective of the <strong>parent</strong> side of the protocol. For example, when you see the <code><strong>send</strong> Msg</code> syntax, it means "when the parent actor sends Msg".</p>
<p>The following example shows one such state machine for the Plugin protocol.</p>
<div class="warning">Note: The following example uses the old ~Destructor syntax, and needs significant reworking to make use of the new __delete__ syntax instead. This is no longer a good example.</div>
<pre><strong>include</strong> <strong>protocol</strong> PPluginInstance;
<strong>sync</strong> <strong>protocol</strong> PPlugin {
<strong>manages</strong> PPluginInstance;
<strong>child</strong>:
<strong>sync</strong> Init() <strong>returns</strong> (int rv);
Deinit();
<strong>sync</strong> PPluginInstance(String type, StringArray args) <strong>returns</strong> (int rv);
// NOTE: state machine follows
<strong>state</strong> START:
<strong>send</strong> Init <strong>goto</strong> IDLE;
<strong>state</strong> IDLE:
<strong>send</strong> PPluginInstance <strong>goto</strong> ACTIVE;
<strong>state</strong> ACTIVE:
<strong>send</strong> PPluginInstance <strong>goto</strong> ACTIVE;
<strong>send</strong> ~PPluginInstance <strong>goto</strong> ACTIVE;
<strong>send</strong> Deinit <strong>goto</strong> DYING;
<strong>state</strong> DYING:
<strong>send</strong> ~PPluginInstance <strong>goto</strong> DYING;
};
</pre>
<p>There are three new syntactic elements, above. First are "state declarations": the code <code><strong>state</strong> FOO:</code> declares a state "FOO". (States are capitalized by convention, not because of syntactic rules.) The first state to be declared is the protocol's "start state"; when an actor is created, its initial state is the "start state."</p>
<p>The second new syntactic element is the <em>trigger</em>. The syntax <code><strong>send</strong> MsgDecl</code> defines a trigger for a state <em>transition</em>; in this case, the trigger is <code><strong>send</strong></code>ing the async or sync message "MsgDecl." The triggers are:</p>
<ol>
<li><code><strong>send</strong></code>ing an async or sync message</li>
<li><code><strong>recv</strong></code>ing an async or sync message</li>
<li><code><strong>call</strong></code>ing an RPC</li>
<li><code><strong>answer</strong></code>ing an RPC</li>
</ol>
<p><strong>Aside</strong>: this is why actor ctors/dtors act like normal messages, with directions etc.: this allows them to be checked against the protocol state machine like any other message.</p>
<p>The third new syntactic element is a state <em>transition</em>. The syntax is: <code><strong>goto</strong> NEXT_STATE</code>. When the trigger preceding this transition occurs, the protocol actor's internal state is changed to, in this case, "NEXT_STATE."</p>
<p>Another example state machine, for PluginInstance, follows.</p>
<pre><strong>sync</strong> <strong>protocol</strong> PluginInstance {
<strong>manager</strong> Plugin;
<strong>child</strong>:
SetWindow(PluginWindow window);
Paint();
<strong>parent</strong>:
<strong>sync</strong> GetBrowserValue(String key) <strong>returns</strong> (String value);
<strong>state</strong> START:
<strong>send</strong> SetWindow <strong>goto</strong> SENT_WINDOW;
<strong>recv</strong> GetBrowserValue <strong>goto</strong> START;
<strong>state</strong> SENT_WINDOW:
<strong>send</strong> SetWindow <strong>goto</strong> SENT_WINDOW;
<strong>send</strong> Paint <strong>goto</strong> SENT_WINDOW;
<strong>recv</strong> GetBrowserValue <strong>goto</strong> SENT_WINDOW;
};
</pre>
<p><br>
Note:</p>
<ul>
<li>The following points will apply when the IPDL compiler fully supports states. It is incomplete as of this writing, on 22 October 2009.</li>
<li>Protocol state machines are optional, but strongly encouraged. Even simple state machines are useful.</li>
<li>All actor states, trigger matching, and transitions are managed by IPDL-generated code. Your C++ code need not manage these things.</li>
<li>All messages sent and received are checked against the protocol's state machine. <em>If a message violates the state machine semantics defined in the IPDL code, the child process containing the child actor will be terminated with extreme prejudice, and all parent actors will be made invalid!</em> It is up to the developer to make sure that this never happens.</li>
<li>Lots of syntactic sugar is possible for state machine definitions. Ping the Electrolysis team if you have a good proposal</li>
</ul>
<div id="s3gt_translate_tooltip" style="left: 315px; top: 1081px;"> </div>
<link href="chrome://s3gt/skin/s3gt_tooltip.css" rel="stylesheet" type="text/css">
|