2011-02-22 Sheriff Bot <webkit.review.bot@gmail.com>
[WebKit-https.git] / Source / WebCore / inspector / CodeGeneratorInspector.pm
1 # Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 package CodeGeneratorInspector;
6
7 use strict;
8
9 use Class::Struct;
10 use File::stat;
11
12 my %typeTransform;
13 $typeTransform{"ApplicationCache"} = {
14     "forward" => "InspectorApplicationCacheAgent",
15     "header" => "InspectorApplicationCacheAgent.h",
16     "domainAccessor" => "m_inspectorAgent->applicationCacheAgent()",
17 };
18 $typeTransform{"CSS"} = {
19     "forward" => "InspectorCSSAgent",
20     "header" => "InspectorCSSAgent.h",
21     "domainAccessor" => "m_inspectorAgent->cssAgent()",
22 };
23 $typeTransform{"Console"} = {
24     "forward" => "InspectorConsoleAgent",
25     "header" => "InspectorConsoleAgent.h",
26     "domainAccessor" => "m_inspectorAgent->consoleAgent()",
27 };
28 $typeTransform{"Debugger"} = {
29     "forward" => "InspectorDebuggerAgent",
30     "header" => "InspectorDebuggerAgent.h",
31     "domainAccessor" => "m_inspectorAgent->debuggerAgent()",
32 };
33 $typeTransform{"BrowserDebugger"} = {
34     "forward" => "InspectorBrowserDebuggerAgent",
35     "header" => "InspectorBrowserDebuggerAgent.h",
36     "domainAccessor" => "m_inspectorAgent->browserDebuggerAgent()",
37 };
38 $typeTransform{"Database"} = {
39     "forward" => "InspectorDatabaseAgent",
40     "header" => "InspectorDatabaseAgent.h",
41     "domainAccessor" => "m_inspectorAgent->databaseAgent()",
42 };
43 $typeTransform{"DOM"} = {
44     "forward" => "InspectorDOMAgent",
45     "header" => "InspectorDOMAgent.h",
46     "domainAccessor" => "m_inspectorAgent->domAgent()",
47 };
48 $typeTransform{"DOMStorage"} = {
49     "forward" => "InspectorDOMStorageAgent",
50     "header" => "InspectorDOMStorageAgent.h",
51     "domainAccessor" => "m_inspectorAgent->domStorageAgent()",
52 };
53 $typeTransform{"FileSystem"} = {
54     "forward" => "InspectorFileSystemAgent",
55     "header" => "InspectorFileSystemAgent.h",
56     "domainAccessor" => "m_inspectorAgent->fileSystemAgent()",
57 };
58 $typeTransform{"InjectedScript"} = {
59     "forwardHeader" => "InjectedScriptHost.h",
60     "domainAccessor" => "m_inspectorAgent->injectedScriptAgent()",
61 };
62 $typeTransform{"Inspector"} = {
63     "forwardHeader" => "InspectorAgent.h",
64     "domainAccessor" => "m_inspectorAgent",
65 };
66 $typeTransform{"Network"} = {
67     "forward" => "InspectorResourceAgent",
68     "header" => "InspectorResourceAgent.h",
69     "domainAccessor" => "m_inspectorAgent->resourceAgent()",
70 };
71 $typeTransform{"Profiler"} = {
72     "forward" => "InspectorProfilerAgent",
73     "header" => "InspectorProfilerAgent.h",
74     "domainAccessor" => "m_inspectorAgent->profilerAgent()",
75 };
76 $typeTransform{"Runtime"} = {
77     "forward" => "InspectorRuntimeAgent",
78     "header" => "InspectorRuntimeAgent.h",
79     "domainAccessor" => "m_inspectorAgent->runtimeAgent()",
80 };
81 $typeTransform{"Timeline"} = {
82     "forward" => "InspectorTimelineAgent",
83     "header" => "InspectorTimelineAgent.h",
84     "domainAccessor" => "m_inspectorAgent->timelineAgent()",
85 };
86
87 $typeTransform{"Frontend"} = {
88     "forward" => "InspectorFrontend",
89     "header" => "InspectorFrontend.h",
90 };
91 $typeTransform{"InspectorClient"} = {
92     "forward" => "InspectorClient",
93     "header" => "InspectorClient.h",
94 };
95 $typeTransform{"PassRefPtr"} = {
96     "forwardHeader" => "wtf/PassRefPtr.h",
97 };
98 $typeTransform{"Object"} = {
99     "param" => "PassRefPtr<InspectorObject>",
100     "variable" => "RefPtr<InspectorObject>",
101     "defaultValue" => "InspectorObject::create()",
102     "forward" => "InspectorObject",
103     "header" => "InspectorValues.h",
104     "JSONType" => "Object",
105     "JSType" => "object",
106     "DocType" => "%s"
107 };
108 $typeTransform{"Array"} = {
109     "param" => "PassRefPtr<InspectorArray>",
110     "variable" => "RefPtr<InspectorArray>",
111     "defaultValue" => "InspectorArray::create()",
112     "forward" => "InspectorArray",
113     "header" => "InspectorValues.h",
114     "JSONType" => "Array",
115     "JSType" => "object",
116     "DocType" => "array of %s"
117 };
118 $typeTransform{"Value"} = {
119     "param" => "PassRefPtr<InspectorValue>",
120     "variable" => "RefPtr<InspectorValue>",
121     "defaultValue" => "InspectorValue::null()",
122     "forward" => "InspectorValue",
123     "header" => "InspectorValues.h",
124     "JSONType" => "Value",
125     "JSType" => "",
126     "DocType" => "value"
127 };
128 $typeTransform{"String"} = {
129     "param" => "const String&",
130     "variable" => "String",
131     "return" => "String",
132     "defaultValue" => "\"\"",
133     "forwardHeader" => "PlatformString.h",
134     "header" => "PlatformString.h",
135     "JSONType" => "String",
136     "JSType" => "string"
137 };
138 $typeTransform{"long"} = {
139     "param" => "long",
140     "variable" => "long",
141     "defaultValue" => "0",
142     "forward" => "",
143     "header" => "",
144     "JSONType" => "Number",
145     "JSType" => "number"
146 };
147 $typeTransform{"int"} = {
148     "param" => "int",
149     "variable" => "int",
150     "defaultValue" => "0",
151     "forward" => "",
152     "header" => "",
153     "JSONType" => "Number",
154     "JSType" => "number"
155 };
156 $typeTransform{"unsigned long"} = {
157     "param" => "unsigned long",
158     "variable" => "unsigned long",
159     "defaultValue" => "0u",
160     "forward" => "",
161     "header" => "",
162     "JSONType" => "Number",
163     "JSType" => "number"
164 };
165 $typeTransform{"unsigned int"} = {
166     "param" => "unsigned int",
167     "variable" => "unsigned int",
168     "defaultValue" => "0u",
169     "forward" => "",
170     "header" => "",
171     "JSONType" => "Number",
172     "JSType" => "number"
173 };
174 $typeTransform{"double"} = {
175     "param" => "double",
176     "variable" => "double",
177     "defaultValue" => "0.0",
178     "forward" => "",
179     "header" => "",
180     "JSONType" => "Number",
181     "JSType" => "number"
182 };
183 $typeTransform{"boolean"} = {
184     "param" => "bool",
185     "variable"=> "bool",
186     "defaultValue" => "false",
187     "forward" => "",
188     "header" => "",
189     "JSONType" => "Boolean",
190     "JSType" => "boolean"
191 };
192 $typeTransform{"void"} = {
193     "forward" => "",
194     "header" => ""
195 };
196
197 # Default License Templates
198
199 my $licenseTemplate = << "EOF";
200 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
201 // Use of this source code is governed by a BSD-style license that can be
202 // found in the LICENSE file.
203 EOF
204
205 my $codeGenerator;
206 my $outputDir;
207 my $outputHeadersDir;
208 my $writeDependencies;
209 my $verbose;
210
211 my $namespace;
212
213 my $backendClassName;
214 my $backendJSStubName;
215 my %backendTypes;
216 my @backendMethods;
217 my @backendMethodsImpl;
218 my %backendMethodSignatures;
219 my $backendConstructor;
220 my @backendConstantDeclarations;
221 my @backendConstantDefinitions;
222 my $backendFooter;
223 my @backendJSStubs;
224
225 my $frontendClassName;
226 my %frontendTypes;
227 my @frontendMethods;
228 my @frontendMethodsImpl;
229 my %frontendMethodSignatures;
230 my $frontendConstructor;
231 my @frontendConstantDeclarations;
232 my @frontendConstantDefinitions;
233 my $frontendFooter;
234
235 my @documentationToc;
236 my @documentationLines;
237
238 # Default constructor
239 sub new
240 {
241     my $object = shift;
242     my $reference = { };
243
244     $codeGenerator = shift;
245     $outputDir = shift;
246     $outputHeadersDir = shift;
247     shift; # $useLayerOnTop
248     shift; # $preprocessor
249     $writeDependencies = shift;
250     $verbose = shift;
251
252     bless($reference, $object);
253     return $reference;
254 }
255
256 # Params: 'idlDocument' struct
257 sub GenerateModule
258 {
259     my $object = shift;
260     my $dataNode = shift;
261
262     $namespace = $dataNode->module;
263     $namespace =~ s/core/WebCore/;
264 }
265
266 # Params: 'idlDocument' struct
267 sub GenerateInterface
268 {
269     my $object = shift;
270     my $interface = shift;
271     my $defines = shift;
272
273     my $className = $interface->name;
274
275     $frontendClassName = "InspectorFrontend";
276     $frontendConstructor = "    ${frontendClassName}(InspectorClient* inspectorClient) : m_inspectorClient(inspectorClient) { }";
277     $frontendFooter = "  private:\n    InspectorClient* m_inspectorClient;";
278     $frontendTypes{"String"} = 1;
279     $frontendTypes{"InspectorClient"} = 1;
280     $frontendTypes{"PassRefPtr"} = 1;
281
282     $backendClassName = "InspectorBackendDispatcher";
283     $backendJSStubName = "InspectorBackendStub";
284     my @backendHead;
285     push(@backendHead, "    ${backendClassName}(InspectorAgent* inspectorAgent) : m_inspectorAgent(inspectorAgent) { }");
286     push(@backendHead, "    void reportProtocolError(const long callId, const String& errorText) const;");
287     push(@backendHead, "    void dispatch(const String& message);");
288     push(@backendHead, "    static bool getCommandName(const String& message, String* result);");
289     $backendConstructor = join("\n", @backendHead);
290     $backendFooter = "    InspectorAgent* m_inspectorAgent;";
291     $backendTypes{"Inspector"} = 1;
292     $backendTypes{"InspectorClient"} = 1;
293     $backendTypes{"PassRefPtr"} = 1;
294     $backendTypes{"Object"} = 1;
295
296     generateFunctions($interface);
297 }
298
299 sub generateFunctions
300 {
301     my $interface = shift;
302
303     foreach my $function (@{$interface->functions}) {
304         if ($function->signature->extendedAttributes->{"notify"}) {
305             generateFrontendFunction($interface, $function);
306         } else {
307             generateBackendFunction($interface, $function);
308         }
309     }
310
311     push(@documentationToc, "<li><a href='#" . $interface->name . "'>" . $interface->name . "</a></li>");
312     push(@documentationLines, "<h2 id='" . $interface->name . "'><a name=" . $interface->name . "></a>" . $interface->name . "</h2>");
313
314     push(@documentationLines, "<h3>Events</h3>");
315     foreach my $function (grep($_->signature->extendedAttributes->{"notify"}, @{$interface->functions}) ) {
316         generateDocumentationEvent($interface, $function);
317     }
318
319     push(@documentationLines, "<h3>Commands</h3>");
320     foreach my $function (grep(!$_->signature->extendedAttributes->{"notify"}, @{$interface->functions})) {
321         generateDocumentationCommand($interface, $function);
322     }
323
324     collectBackendJSStubFunctions($interface);
325 }
326
327 sub generateFrontendFunction
328 {
329     my $interface = shift;
330     my $function = shift;
331
332     my $functionName = $function->signature->name;
333
334     my $domain = $interface->name;
335     my @argsFiltered = grep($_->direction eq "out", @{$function->parameters}); # just keep only out parameters for frontend interface.
336     map($frontendTypes{$_->type} = 1, @argsFiltered); # register required types.
337     my $arguments = join(", ", map(typeTraits($_->type, "param") . " " . $_->name, @argsFiltered)); # prepare arguments for function signature.
338
339     my $signature = "    void ${functionName}(${arguments});";
340     !$frontendMethodSignatures{${signature}} || die "Duplicate frontend function was detected for signature '$signature'.";
341     $frontendMethodSignatures{${signature}} = 1;
342     push(@frontendMethods, $signature);
343
344     my @function;
345     push(@function, "void ${frontendClassName}::${functionName}(${arguments})");
346     push(@function, "{");
347     push(@function, "    RefPtr<InspectorObject> ${functionName}Message = InspectorObject::create();");
348     push(@function, "    ${functionName}Message->setString(\"type\", \"event\");");
349     push(@function, "    ${functionName}Message->setString(\"domain\", \"$domain\");");
350     push(@function, "    ${functionName}Message->setString(\"event\", \"$functionName\");");
351     push(@function, "    RefPtr<InspectorObject> payloadDataObject = InspectorObject::create();");
352     my @pushArguments = map("    payloadDataObject->set" . typeTraits($_->type, "JSONType") . "(\"" . $_->name . "\", " . $_->name . ");", @argsFiltered);
353     push(@function, @pushArguments);
354     push(@function, "    ${functionName}Message->setObject(\"data\", payloadDataObject);");
355     push(@function, "    m_inspectorClient->sendMessageToFrontend(${functionName}Message->toJSONString());");
356
357     push(@function, "}");
358     push(@function, "");
359     push(@frontendMethodsImpl, @function);
360 }
361
362 sub generateDocumentationEvent
363 {
364     my $interface = shift;
365     my $function = shift;
366
367     my $functionName = $function->signature->name;
368     my $domain = $interface->name;
369
370     my @argsFiltered = grep($_->direction eq "out", @{$function->parameters});
371
372     my @lines;
373     push(@lines, "<h4>" . $interface->name . "." . ${functionName} . "</h4>");
374     my $doc = $function->signature->extendedAttributes->{"doc"};
375     if ($doc) {
376         push(@lines, $doc);
377     }
378
379     push(@lines, "<pre style='background: lightGrey; padding: 10px'>");
380     push(@lines, "{");
381     push(@lines, "    seq: &lt;number&gt;,");
382     push(@lines, "    type: \"event\",");
383     push(@lines, "    domain: \"$domain\",");
384     if (scalar(@argsFiltered)) {
385         push(@lines, "    event: \"${functionName}\",");
386         push(@lines, "    data: {");
387         my @parameters;
388         foreach my $parameter (@argsFiltered) {
389             push(@parameters, "        " . parameterDocLine($parameter));
390         }
391         push(@lines, join(",\n", @parameters));
392         push(@lines, "    }");
393     } else {
394         push(@lines, "    event: \"${functionName}\"");
395     }
396     push(@lines, "}");
397     push(@lines, "</pre>");
398     push(@documentationLines, @lines);
399 }
400
401 sub camelCase
402 {
403     my $value = shift;
404     $value =~ s/\b(\w)/\U$1/g; # make a camel-case name for type name
405     $value =~ s/ //g;
406     return $value;
407 }
408
409 sub generateBackendFunction
410 {
411     my $interface = shift;
412     my $function = shift;
413
414     my $functionName = $function->signature->name;
415     my $fullQualifiedFunctionName = $interface->name . "_" . $function->signature->name;
416
417     push(@backendConstantDeclarations, "    static const char* ${fullQualifiedFunctionName}Cmd;");
418     push(@backendConstantDefinitions, "const char* ${backendClassName}::${fullQualifiedFunctionName}Cmd = \"${functionName}\";");
419
420     map($backendTypes{$_->type} = 1, @{$function->parameters}); # register required types
421     my @inArgs = grep($_->direction eq "in" && !($_->name eq "callId") , @{$function->parameters});
422     my @outArgs = grep($_->direction eq "out", @{$function->parameters});
423     
424     my $signature = "    void ${fullQualifiedFunctionName}(long callId, InspectorObject* requestMessageObject);";
425     !$backendMethodSignatures{${signature}} || die "Duplicate function was detected for signature '$signature'.";
426     $backendMethodSignatures{${signature}} = "$fullQualifiedFunctionName";
427     push(@backendMethods, ${signature});
428
429     my @function;
430     my $requestMessageObject = scalar(@inArgs) ? " requestMessageObject" : "";
431     push(@function, "void ${backendClassName}::${fullQualifiedFunctionName}(long callId, InspectorObject*$requestMessageObject)");
432     push(@function, "{");
433     push(@function, "    RefPtr<InspectorArray> protocolErrors = InspectorArray::create();");
434     push(@function, "");
435
436     my $domain = $interface->name;
437     my $domainAccessor = typeTraits($domain, "domainAccessor");
438     $backendTypes{$domain} = 1;
439     push(@function, "    if (!$domainAccessor)");
440     push(@function, "        protocolErrors->pushString(\"Protocol Error: $domain handler is not available.\");");
441     push(@function, "");
442
443     # declare local variables for out arguments.
444     push(@function, map("    " . typeTraits($_->type, "variable") . " " . $_->name . " = " . typeTraits($_->type, "defaultValue") . ";", @outArgs));
445
446     my $indent = "";
447     if (scalar(@inArgs)) {
448         push(@function, "    if (RefPtr<InspectorObject> argumentsContainer = requestMessageObject->getObject(\"arguments\")) {");
449
450         foreach my $parameter (@inArgs) {
451             my $name = $parameter->name;
452             my $type = $parameter->type;
453             my $typeString = camelCase($parameter->type);
454             push(@function, "        " . typeTraits($type, "variable") . " $name = get$typeString(argumentsContainer.get(), \"$name\", protocolErrors.get());");
455         }
456         push(@function, "");
457         $indent = "    ";
458     }
459
460     my $args = join(", ", (map($_->name, @inArgs), map("&" . $_->name, @outArgs)));
461     push(@function, "$indent    if (!protocolErrors->length())");
462     push(@function, "$indent        $domainAccessor->$functionName($args);");
463     if (scalar(@inArgs)) {
464         push(@function, "    } else {");
465         push(@function, "        protocolErrors->pushString(\"Protocol Error: 'arguments' property with type 'object' was not found.\");");
466         push(@function, "    }");
467     }
468
469     push(@function, "    // use InspectorFrontend as a marker of WebInspector availability");
470     push(@function, "    if ((callId || protocolErrors->length()) && m_inspectorAgent->hasFrontend()) {");
471     push(@function, "        RefPtr<InspectorObject> responseMessage = InspectorObject::create();");
472     push(@function, "        responseMessage->setNumber(\"seq\", callId);");
473     push(@function, "        responseMessage->setString(\"domain\", \"$domain\");");
474     push(@function, "        responseMessage->setBoolean(\"success\", !protocolErrors->length());");
475     push(@function, "");
476     push(@function, "        if (protocolErrors->length())");
477     push(@function, "            responseMessage->setArray(\"errors\", protocolErrors);");
478     if (scalar(@outArgs)) {
479         push(@function, "        else {");
480         push(@function, "            RefPtr<InspectorObject> responseData = InspectorObject::create();");
481         push(@function, map("            responseData->set" . typeTraits($_->type, "JSONType") . "(\"" . $_->name . "\", " . $_->name . ");", @outArgs));
482         push(@function, "            responseMessage->setObject(\"data\", responseData);");
483         push(@function, "        }");
484     }
485     push(@function, "        m_inspectorAgent->inspectorClient()->sendMessageToFrontend(responseMessage->toJSONString());");
486     push(@function, "    }");
487
488
489     push(@function, "}");
490     push(@function, "");
491     push(@backendMethodsImpl, @function);
492 }
493
494 sub generateDocumentationCommand
495 {
496     my $interface = shift;
497     my $function = shift;
498
499     my $functionName = $function->signature->name;
500     my $domain = $interface->name;
501
502     my @lines;
503
504     push(@lines, "<h4>" . $interface->name . "." . ${functionName} . "</h4>");
505     my $doc = $function->signature->extendedAttributes->{"doc"};
506     if ($doc) {
507         push(@lines, $doc);
508     }
509
510     my @inArgs = grep($_->direction eq "in" && !($_->name eq "callId") , @{$function->parameters});
511     push(@lines, "<pre style='background: lightGrey; padding: 10px'>");
512     push(@lines, "request: {");
513     push(@lines, "    seq: &lt;number&gt;,");
514     push(@lines, "    type: \"request\",");
515     push(@lines, "    domain: \"" . $interface->name . "\",");
516     if (scalar(@inArgs)) {
517         push(@lines, "    command: \"${functionName}\",");
518         push(@lines, "    arguments: {");
519         my @parameters;
520         foreach my $parameter (@inArgs) {
521             push(@parameters, "        " . parameterDocLine($parameter));
522         }
523         push(@lines, join(",\n", @parameters));
524         push(@lines, "    }");
525     } else {
526         push(@lines, "    command: \"${functionName}\"");
527     }
528     push(@lines, "}");
529
530     my @outArgs = grep($_->direction eq "out", @{$function->parameters});    
531     push(@lines, "");
532     push(@lines, "response: {");
533     push(@lines, "    seq: &lt;number&gt;,");
534     if (scalar(@outArgs)) {
535         push(@lines, "    type: \"response\",");
536         push(@lines, "    body: {");
537             my @parameters;
538             foreach my $parameter (@outArgs) {
539                 push(@parameters, "        " . parameterDocLine($parameter));
540             }
541             push(@lines, join(",\n", @parameters));
542         push(@lines, "    }");
543     } else {
544         push(@lines, "    type: \"response\"");
545     }
546     push(@lines, "}");
547     push(@lines, "</pre>");
548
549     push(@documentationLines, @lines);
550 }
551
552 sub generateBackendReportProtocolError
553 {
554     my $reportProtocolError = << "EOF";
555
556 void ${backendClassName}::reportProtocolError(const long callId, const String& errorText) const
557 {
558     RefPtr<InspectorObject> message = InspectorObject::create();
559     message->setNumber("seq", callId);
560     message->setBoolean("success", false);
561     RefPtr<InspectorArray> errors = InspectorArray::create();
562     errors->pushString(errorText);
563     message->setArray("errors", errors);
564     m_inspectorAgent->inspectorClient()->sendMessageToFrontend(message->toJSONString());
565 }
566 EOF
567     return split("\n", $reportProtocolError);
568 }
569
570 sub generateArgumentGetters
571 {
572     my $type = shift;
573     my $json = typeTraits($type, "JSONType");
574     my $variable = typeTraits($type, "variable");
575     my $defaultValue = typeTraits($type, "defaultValue");
576     my $return  = typeTraits($type, "return") ? typeTraits($type, "return") : typeTraits($type, "param");
577
578     my $typeString = camelCase($type);
579     push(@backendConstantDeclarations, "    $return get$typeString(InspectorObject* object, const String& name, InspectorArray* protocolErrors);");
580     my $getterBody = << "EOF";
581
582 $return InspectorBackendDispatcher::get$typeString(InspectorObject* object, const String& name, InspectorArray* protocolErrors)
583 {
584     ASSERT(object);
585     ASSERT(protocolErrors);
586
587     $variable value = $defaultValue;
588     InspectorObject::const_iterator end = object->end();
589     InspectorObject::const_iterator valueIterator = object->find(name);
590
591     if (valueIterator == end)
592         protocolErrors->pushString(String::format("Protocol Error: Argument '\%s' with type '$json' was not found.", name.utf8().data()));
593     else {
594         if (!valueIterator->second->as$json(&value))
595             protocolErrors->pushString(String::format("Protocol Error: Argument '\%s' has wrong type. It should be '$json'.", name.utf8().data()));
596     }
597     return value;
598 }
599 EOF
600
601     return split("\n", $getterBody);
602 }
603
604 sub generateBackendDispatcher
605 {
606     my @body;
607     my @mapEntries = map("        dispatchMap.add(${_}Cmd, &${backendClassName}::$_);", map ($backendMethodSignatures{$_}, @backendMethods));
608     my $mapEntries = join("\n", @mapEntries);
609
610     my $backendDispatcherBody = << "EOF";
611 void ${backendClassName}::dispatch(const String& message)
612 {
613     typedef void (${backendClassName}::*CallHandler)(long callId, InspectorObject* messageObject);
614     typedef HashMap<String, CallHandler> DispatchMap;
615     DEFINE_STATIC_LOCAL(DispatchMap, dispatchMap, );
616     long callId = 0;
617
618     if (dispatchMap.isEmpty()) {
619 $mapEntries
620     }
621
622     RefPtr<InspectorValue> parsedMessage = InspectorValue::parseJSON(message);
623     if (!parsedMessage) {
624         reportProtocolError(callId, "Protocol Error: Invalid message format. Message should be in JSON format.");
625         return;
626     }
627
628     RefPtr<InspectorObject> messageObject = parsedMessage->asObject();
629     if (!messageObject) {
630         reportProtocolError(callId, "Protocol Error: Invalid message format. The message should be a JSONified object.");
631         return;
632     }
633
634     RefPtr<InspectorValue> commandValue = messageObject->get("command");
635     if (!commandValue) {
636         reportProtocolError(callId, "Protocol Error: Invalid message format. 'command' property wasn't found.");
637         return;
638     }
639
640     String command;
641     if (!commandValue->asString(&command)) {
642         reportProtocolError(callId, "Protocol Error: Invalid message format. The type of 'command' property should be string.");
643         return;
644     }
645
646     RefPtr<InspectorValue> callIdValue = messageObject->get("seq");
647     if (!callIdValue) {
648         reportProtocolError(callId, "Protocol Error: Invalid message format. 'seq' property was not found in the request.");
649         return;
650     }
651
652     if (!callIdValue->asNumber(&callId)) {
653         reportProtocolError(callId, "Protocol Error: Invalid message format. The type of 'seq' property should be number.");
654         return;
655     }
656
657     HashMap<String, CallHandler>::iterator it = dispatchMap.find(command);
658     if (it == dispatchMap.end()) {
659         reportProtocolError(callId, makeString("Protocol Error: Invalid command was received. '", command, "' wasn't found."));
660         return;
661     }
662
663     ((*this).*it->second)(callId, messageObject.get());
664 }
665 EOF
666     return split("\n", $backendDispatcherBody);
667 }
668
669 sub generateBackendMessageParser
670 {
671     my $messageParserBody = << "EOF";
672 bool ${backendClassName}::getCommandName(const String& message, String* result)
673 {
674     RefPtr<InspectorValue> value = InspectorValue::parseJSON(message);
675     if (!value)
676         return false;
677
678     RefPtr<InspectorObject> object = value->asObject();
679     if (!object)
680         return false;
681
682     RefPtr<InspectorValue> commandValue = object->get("command");
683     if (!commandValue)
684         return false;
685
686     return commandValue->asString(result);
687 }
688 EOF
689     return split("\n", $messageParserBody);
690 }
691
692 sub collectBackendJSStubFunctions
693 {
694     my $interface = shift;
695     my @functions = grep(!$_->signature->extendedAttributes->{"notify"}, @{$interface->functions});
696     my $domain = $interface->name;
697
698     foreach my $function (@functions) {
699         my $name = $function->signature->name;
700         my $argumentNames = join(",", map("\"" . $_->name . "\": \"" . typeTraits($_->type, "JSType") . "\"", grep($_->direction eq "in", @{$function->parameters})));
701         push(@backendJSStubs, "    this._registerDelegate('{" .
702             "\"seq\": 0, " .
703             "\"domain\": \"$domain\", " .
704             "\"command\": \"$name\", " .
705             "\"arguments\": {$argumentNames}" .
706         "}');");
707     }
708 }
709
710 sub generateBackendStubJS
711 {
712     my $JSStubs = join("\n", @backendJSStubs);
713     my $inspectorBackendStubJS = << "EOF";
714 $licenseTemplate
715
716 InspectorBackendStub = function()
717 {
718     this._lastCallbackId = 1;
719     this._pendingResponsesCount = 0;
720     this._callbacks = {};
721     this._domainDispatchers = {};
722 $JSStubs
723 }
724
725 InspectorBackendStub.prototype = {
726     _wrap: function(callback)
727     {
728         var callbackId = this._lastCallbackId++;
729         ++this._pendingResponsesCount;
730         this._callbacks[callbackId] = callback || function() {};
731         return callbackId;
732     },
733
734     _registerDelegate: function(commandInfo)
735     {
736         var commandObject = JSON.parse(commandInfo);
737         var agentName = commandObject.domain + "Agent";
738         if (!window[agentName])
739             window[agentName] = {};
740         window[agentName][commandObject.command] = this.sendMessageToBackend.bind(this, commandInfo);
741     },
742
743     sendMessageToBackend: function()
744     {
745         var args = Array.prototype.slice.call(arguments);
746         var request = JSON.parse(args.shift());
747
748         for (var key in request.arguments) {
749             if (args.length === 0) {
750                 console.error("Protocol Error: Invalid number of arguments for '" + request.domain + "Agent." + request.command + "' call. It should have the next arguments '" + JSON.stringify(request.arguments) + "'.");
751                 return;
752             }
753             var value = args.shift();
754             if (request.arguments[key] && typeof value !== request.arguments[key]) {
755                 console.error("Protocol Error: Invalid type of argument '" + key + "' for '" + request.domain + "Agent." + request.command + "' call. It should be '" + request.arguments[key] + "' but it is '" + typeof value + "'.");
756                 return;
757             }
758             request.arguments[key] = value;
759         }
760
761         var callback;
762         if (args.length === 1) {
763             if (typeof args[0] !== "function" && typeof args[0] !== "undefined") {
764                 console.error("Protocol Error: Optional callback argument for '" + request.domain + "Agent." + request.command + "' call should be a function but its type is '" + typeof args[0] + "'.");
765                 return;
766             }
767             callback = args[0];
768         }
769         request.seq = this._wrap(callback || function() {});
770
771         if (window.dumpInspectorProtocolMessages)
772             console.log("frontend: " + JSON.stringify(request));
773
774         var message = JSON.stringify(request);
775         InspectorFrontendHost.sendMessageToBackend(message);
776     },
777
778     registerDomainDispatcher: function(domain, dispatcher)
779     {
780         this._domainDispatchers[domain] = dispatcher;
781     },
782
783     dispatch: function(message)
784     {
785         if (window.dumpInspectorProtocolMessages)
786             console.log("backend: " + ((typeof message === "string") ? message : JSON.stringify(message)));
787
788         var messageObject = (typeof message === "string") ? JSON.parse(message) : message;
789
790         var arguments = [];
791         if (messageObject.data)
792             for (var key in messageObject.data)
793                 arguments.push(messageObject.data[key]);
794
795         if ("seq" in messageObject) { // just a response for some request
796             if (messageObject.success)
797                 this._callbacks[messageObject.seq].apply(null, arguments);
798             else
799                 this.reportProtocolError(messageObject);
800
801             --this._pendingResponsesCount;
802             delete this._callbacks[messageObject.seq];
803
804             if (this._scripts && !this._pendingResponsesCount)
805                 this.runAfterPendingDispatches();
806
807             return;
808         }
809
810         if (messageObject.type === "event") {
811             if (!(messageObject.domain in this._domainDispatchers)) {
812                 console.error("Protocol Error: the message is for non-existing domain '" + messageObject.domain + "'");
813                 return;
814             }
815             var dispatcher = this._domainDispatchers[messageObject.domain];
816             if (!(messageObject.event in dispatcher)) {
817                 console.error("Protocol Error: Attempted to dispatch an unimplemented method '" + messageObject.domain + "." + messageObject.event + "'");
818                 return;
819             }
820             dispatcher[messageObject.event].apply(dispatcher, arguments);
821         }
822     },
823
824     reportProtocolError: function(messageObject)
825     {
826         console.error("Protocol Error: InspectorBackend request with seq = " + messageObject.seq + " failed.");
827         for (var i = 0; i < messageObject.errors.length; ++i)
828             console.error("    " + messageObject.errors[i]);
829     },
830
831     runAfterPendingDispatches: function(script)
832     {
833         if (!this._scripts)
834             this._scripts = [];
835
836         if (script)
837             this._scripts.push(script);
838
839         if (!this._pendingResponsesCount) {
840             var scripts = this._scripts;
841             this._scripts = []
842             for (var id = 0; id < scripts.length; ++id)
843                  scripts[id].call(this);
844         }
845     }
846 }
847
848 InspectorBackend = new InspectorBackendStub();
849
850 EOF
851     return split("\n", $inspectorBackendStubJS);
852 }
853
854 sub generateHeader
855 {
856     my $className = shift;
857     my $types = shift;
858     my $constructor = shift;
859     my $constants = shift;
860     my $methods = shift;
861     my $footer = shift;
862
863     my $forwardHeaders = join("\n", sort(map("#include <" . typeTraits($_, "forwardHeader") . ">", grep(typeTraits($_, "forwardHeader"), keys %{$types}))));
864     my $forwardDeclarations = join("\n", sort(map("class " . typeTraits($_, "forward") . ";", grep(typeTraits($_, "forward"), keys %{$types}))));
865     my $constantDeclarations = join("\n", @{$constants});
866     my $methodsDeclarations = join("\n", @{$methods});
867
868     my $headerBody = << "EOF";
869 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
870 // Use of this source code is governed by a BSD-style license that can be
871 // found in the LICENSE file.
872 #ifndef ${className}_h
873 #define ${className}_h
874
875 ${forwardHeaders}
876
877 namespace $namespace {
878
879 $forwardDeclarations
880
881 class $className {
882 public:
883 $constructor
884
885 $constantDeclarations
886 $methodsDeclarations
887
888 $footer
889 };
890
891 } // namespace $namespace
892 #endif // !defined(${className}_h)
893
894 EOF
895     return $headerBody;
896 }
897
898 sub generateSource
899 {
900     my $className = shift;
901     my $types = shift;
902     my $constants = shift;
903     my $methods = shift;
904
905     my @sourceContent = split("\r", $licenseTemplate);
906     push(@sourceContent, "\n#include \"config.h\"");
907     push(@sourceContent, "#include \"$className.h\"");
908     push(@sourceContent, "#include <wtf/text/StringConcatenate.h>");
909     push(@sourceContent, "#include <wtf/text/CString.h>");
910     push(@sourceContent, "");
911     push(@sourceContent, "#if ENABLE(INSPECTOR)");
912     push(@sourceContent, "");
913
914     my %headers;
915     foreach my $type (keys %{$types}) {
916         $headers{"#include \"" . typeTraits($type, "header") . "\""} = 1 if !typeTraits($type, "header") eq  "";
917     }
918     push(@sourceContent, sort keys %headers);
919     push(@sourceContent, "");
920     push(@sourceContent, "namespace $namespace {");
921     push(@sourceContent, "");
922     push(@sourceContent, join("\n", @{$constants}));
923     push(@sourceContent, "");
924     push(@sourceContent, @{$methods});
925     push(@sourceContent, "");
926     push(@sourceContent, "} // namespace $namespace");
927     push(@sourceContent, "");
928     push(@sourceContent, "#endif // ENABLE(INSPECTOR)");
929     push(@sourceContent, "");
930     return @sourceContent;
931 }
932
933 sub typeTraits
934 {
935     my $type = shift;
936     my $trait = shift;
937     return $typeTransform{$type}->{$trait};
938 }
939
940 sub parameterDocType
941 {
942     my $parameter = shift;
943     my $subtype = $parameter->extendedAttributes->{"type"};
944     if ($subtype) {
945         my $pattern = typeTraits($parameter->type, "DocType");
946         return sprintf($pattern, "&lt;$subtype&gt;");
947     }
948
949     my $subtypeRef = $parameter->extendedAttributes->{"typeRef"};
950     if ($subtypeRef) {
951         my $pattern = typeTraits($parameter->type, "DocType");
952         return sprintf($pattern, "&lt;<a href='#$subtypeRef'>" . $subtypeRef . "</a>&gt;");
953     }
954
955     return "&lt;" . typeTraits($parameter->type, "JSType") . "&gt;";
956 }
957
958 sub parameterDocLine
959 {
960     my $parameter = shift;
961
962     my $result = $parameter->name . ": " . parameterDocType($parameter);
963     my $doc = $parameter->extendedAttributes->{"doc"};
964     if ($doc) {
965         $result = $result . " // " . $doc;
966     }
967     return $result;
968 }
969
970 sub finish
971 {
972     my $object = shift;
973
974     push(@backendMethodsImpl, generateBackendDispatcher());
975     push(@backendMethodsImpl, generateBackendReportProtocolError());
976
977     open(my $SOURCE, ">$outputDir/$frontendClassName.cpp") || die "Couldn't open file $outputDir/$frontendClassName.cpp";
978     print $SOURCE join("\n", generateSource($frontendClassName, \%frontendTypes, \@frontendConstantDefinitions, \@frontendMethodsImpl));
979     close($SOURCE);
980     undef($SOURCE);
981
982     open(my $HEADER, ">$outputHeadersDir/$frontendClassName.h") || die "Couldn't open file $outputHeadersDir/$frontendClassName.h";
983     print $HEADER generateHeader($frontendClassName, \%frontendTypes, $frontendConstructor, \@frontendConstantDeclarations, \@frontendMethods, $frontendFooter);
984     close($HEADER);
985     undef($HEADER);
986
987     # Make dispatcher methods private on the backend.
988     push(@backendConstantDeclarations, "");
989     push(@backendConstantDeclarations, "private:");
990
991     foreach my $type (keys %backendTypes) {
992         if (typeTraits($type, "JSONType")) {
993             push(@backendMethodsImpl, generateArgumentGetters($type));
994         }
995     }
996
997     push(@backendMethodsImpl, generateBackendMessageParser());
998     push(@backendMethodsImpl, "");
999
1000     push(@backendConstantDeclarations, "");
1001
1002     open($SOURCE, ">$outputDir/$backendClassName.cpp") || die "Couldn't open file $outputDir/$backendClassName.cpp";
1003     print $SOURCE join("\n", generateSource($backendClassName, \%backendTypes, \@backendConstantDefinitions, \@backendMethodsImpl));
1004     close($SOURCE);
1005     undef($SOURCE);
1006
1007     open($HEADER, ">$outputHeadersDir/$backendClassName.h") || die "Couldn't open file $outputHeadersDir/$backendClassName.h";
1008     print $HEADER join("\n", generateHeader($backendClassName, \%backendTypes, $backendConstructor, \@backendConstantDeclarations, \@backendMethods, $backendFooter));
1009     close($HEADER);
1010     undef($HEADER);
1011
1012     open(my $JS_STUB, ">$outputDir/$backendJSStubName.js") || die "Couldn't open file $outputDir/$backendJSStubName.js";
1013     print $JS_STUB join("\n", generateBackendStubJS());
1014     close($JS_STUB);
1015     undef($JS_STUB);
1016
1017     open(my $DOCS, ">$outputDir/WebInspectorProtocol.html") || die "Couldn't open file $outputDir/WebInspectorProtocol.html";
1018     print $DOCS "<ol class='toc' style='list-style: none; padding: 0'>";
1019     print $DOCS join("\n", @documentationToc);
1020     print $DOCS "</ol>";
1021     print $DOCS join("\n", @documentationLines);
1022     close($DOCS);
1023     undef($DOCS);
1024 }
1025
1026 1;