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