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