2011-04-12 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
379         foreach my $parameter (@argsFiltered) {
380             my $optional = $parameter->extendedAttributes->{"optional"} ? "if (" . $parameter->name . ")\n        " : "";
381             push(@function, "    " . $optional . "paramsObject->set" . typeTraits($parameter->type, "JSONType") . "(\"" . $parameter->name . "\", " . $parameter->name . ");");
382         }
383         push(@function, "    ${functionName}Message->setObject(\"params\", paramsObject);");
384     }
385     push(@function, "    m_inspectorFrontendChannel->sendMessageToFrontend(${functionName}Message->toJSONString());");
386     push(@function, "}");
387     push(@function, "");
388     push(@frontendMethodsImpl, @function);
389 }
390
391 sub camelCase
392 {
393     my $value = shift;
394     $value =~ s/\b(\w)/\U$1/g; # make a camel-case name for type name
395     $value =~ s/ //g;
396     return $value;
397 }
398
399 sub generateBackendFunction
400 {
401     my $interface = shift;
402     my $function = shift;
403
404     my $functionName = $function->signature->name;
405     my $fullQualifiedFunctionName = $interface->name . "_" . $function->signature->name;
406     my $fullQualifiedFunctionNameDot = $interface->name . "." . $function->signature->name;
407
408     push(@backendConstantDeclarations, "    static const char* ${fullQualifiedFunctionName}Cmd;");
409     push(@backendConstantDefinitions, "const char* ${backendClassName}::${fullQualifiedFunctionName}Cmd = \"${fullQualifiedFunctionNameDot}\";");
410
411     map($backendTypes{$_->type} = 1, @{$function->parameters}); # register required types
412     my @inArgs = grep($_->direction eq "in", @{$function->parameters});
413     my @outArgs = grep($_->direction eq "out", @{$function->parameters});
414     
415     my $signature = "    void ${fullQualifiedFunctionName}(long callId, InspectorObject* requestMessageObject);";
416     !$backendMethodSignatures{${signature}} || die "Duplicate function was detected for signature '$signature'.";
417     $backendMethodSignatures{${signature}} = "$fullQualifiedFunctionName";
418     push(@backendMethods, ${signature});
419
420     my @function;
421     my $requestMessageObject = scalar(@inArgs) ? " requestMessageObject" : "";
422     push(@function, "void ${backendClassName}::${fullQualifiedFunctionName}(long callId, InspectorObject*$requestMessageObject)");
423     push(@function, "{");
424     push(@function, "    RefPtr<InspectorArray> protocolErrors = InspectorArray::create();");
425     push(@function, "");
426
427     my $domain = $interface->name;
428     my $domainAccessor = typeTraits($domain, "domainAccessor");
429     $backendTypes{$domain} = 1;
430     $backendDomains{$domain} = 1;
431     push(@function, "    if (!$domainAccessor)");
432     push(@function, "        protocolErrors->pushString(\"$domain handler is not available.\");");
433     push(@function, "");
434
435     # declare local variables for out arguments.
436     if (scalar(@outArgs)) {
437         push(@function, map("    " . typeTraits($_->type, "variable") . " out_" . $_->name . " = " . typeTraits($_->type, "defaultValue") . ";", @outArgs));
438         push(@function, "");
439     }
440     push(@function, "    ErrorString error;");
441     push(@function, "");
442
443     my $indent = "";
444     if (scalar(@inArgs)) {
445         push(@function, "    if (RefPtr<InspectorObject> paramsContainer = requestMessageObject->getObject(\"params\")) {");
446
447         foreach my $parameter (@inArgs) {
448             my $name = $parameter->name;
449             my $type = $parameter->type;
450             my $typeString = camelCase($parameter->type);
451             my $optional = $parameter->extendedAttributes->{"optional"} ? "true" : "false";
452             push(@function, "        " . typeTraits($type, "variable") . " in_$name = get$typeString(paramsContainer.get(), \"$name\", $optional, protocolErrors.get());");
453         }
454         push(@function, "");
455         $indent = "    ";
456     }
457
458
459     my $args = join(", ",
460                     ("&error",
461                      map(($_->extendedAttributes->{"optional"} ? "&" : "") . "in_" . $_->name, @inArgs),
462                      map("&out_" . $_->name, @outArgs)));
463
464     push(@function, "$indent    if (!protocolErrors->length())");
465     push(@function, "$indent        $domainAccessor->$functionName($args);");
466     if (scalar(@inArgs)) {
467         push(@function, "    } else");
468         push(@function, "        protocolErrors->pushString(\"'params' property with type 'object' was not found.\");");
469     }
470
471     push(@function, "");
472     push(@function, "    // use InspectorFrontend as a marker of WebInspector availability");
473     push(@function, "");
474     push(@function, "    if (protocolErrors->length()) {");
475     push(@function, "        reportProtocolError(&callId, InvalidParams, protocolErrors);");
476     push(@function, "        return;");
477     push(@function, "    }");
478     push(@function, "");
479     push(@function, "    if (error.length()) {");
480     push(@function, "        reportProtocolError(&callId, ServerError, error);");
481     push(@function, "        return;");
482     push(@function, "    }");
483     push(@function, "");
484     push(@function, "    RefPtr<InspectorObject> responseMessage = InspectorObject::create();");
485     push(@function, "    RefPtr<InspectorObject> result = InspectorObject::create();");
486     push(@function, map("        result->set" . typeTraits($_->type, "JSONType") . "(\"" . $_->name . "\", out_" . $_->name . ");", @outArgs));
487     push(@function, "    responseMessage->setObject(\"result\", result);");
488     push(@function, "");
489     push(@function, "    responseMessage->setNumber(\"id\", callId);");
490     push(@function, "    m_inspectorFrontendChannel->sendMessageToFrontend(responseMessage->toJSONString());");
491     push(@function, "}");
492     push(@function, "");
493     push(@backendMethodsImpl, @function);
494 }
495
496 sub generateBackendReportProtocolError
497 {
498     my $reportProtocolError = << "EOF";
499
500 void ${backendClassName}::reportProtocolError(const long* const callId, CommonErrorCode code, const String& customText) const
501 {
502     RefPtr<InspectorArray> data = InspectorArray::create();
503     data->pushString(customText);
504     reportProtocolError(callId, code, data.release());
505 }
506
507 void ${backendClassName}::reportProtocolError(const long* const callId, CommonErrorCode code, PassRefPtr<InspectorArray> data) const
508 {
509     DEFINE_STATIC_LOCAL(Vector<String>,s_commonErrors,);
510     if (!s_commonErrors.size()) {
511         s_commonErrors.insert(ParseError, "{\\\"code\\\":-32700,\\\"message\\\":\\\"Parse error.\\\"}");
512         s_commonErrors.insert(InvalidRequest, "{\\\"code\\\":-32600,\\\"message\\\":\\\"Invalid Request.\\\"}");
513         s_commonErrors.insert(MethodNotFound, "{\\\"code\\\":-32601,\\\"message\\\":\\\"Method not found.\\\"}");
514         s_commonErrors.insert(InvalidParams, "{\\\"code\\\":-32602,\\\"message\\\":\\\"Invalid params.\\\"}");
515         s_commonErrors.insert(InternalError, "{\\\"code\\\":-32603,\\\"message\\\":\\\"Internal error.\\\"}");
516         s_commonErrors.insert(ServerError, "{\\\"code\\\":-32000,\\\"message\\\":\\\"Server error.\\\"}");
517     }
518     ASSERT(code >=0);
519     ASSERT((unsigned)code < s_commonErrors.size());
520     ASSERT(s_commonErrors[code]);
521     ASSERT(InspectorObject::parseJSON(s_commonErrors[code]));
522     RefPtr<InspectorObject> error = InspectorObject::parseJSON(s_commonErrors[code])->asObject();
523     ASSERT(error);
524     error->setArray("data", data);
525     RefPtr<InspectorObject> message = InspectorObject::create();
526     message->setObject("error", error);
527     if (callId)
528         message->setNumber("id", *callId);
529     else
530         message->setValue("id", InspectorValue::null());
531     m_inspectorFrontendChannel->sendMessageToFrontend(message->toJSONString());
532 }
533 EOF
534     return split("\n", $reportProtocolError);
535 }
536
537 sub generateArgumentGetters
538 {
539     my $type = shift;
540     my $json = typeTraits($type, "JSONType");
541     my $variable = typeTraits($type, "variable");
542     my $defaultValue = typeTraits($type, "defaultValue");
543     my $return  = typeTraits($type, "return") ? typeTraits($type, "return") : typeTraits($type, "param");
544
545     my $typeString = camelCase($type);
546     push(@backendConstantDeclarations, "    $return get$typeString(InspectorObject* object, const String& name, bool optional, InspectorArray* protocolErrors);");
547     my $getterBody = << "EOF";
548
549 $return InspectorBackendDispatcher::get$typeString(InspectorObject* object, const String& name, bool optional, InspectorArray* protocolErrors)
550 {
551     ASSERT(object);
552     ASSERT(protocolErrors);
553
554     $variable value = $defaultValue;
555     InspectorObject::const_iterator end = object->end();
556     InspectorObject::const_iterator valueIterator = object->find(name);
557
558     if (valueIterator == end) {
559         if (!optional)
560             protocolErrors->pushString(String::format("Parameter '\%s' with type '$json' was not found.", name.utf8().data()));
561         return value;
562     }
563
564     if (!valueIterator->second->as$json(&value))
565         protocolErrors->pushString(String::format("Parameter '\%s' has wrong type. It should be '$json'.", name.utf8().data()));
566     return value;
567 }
568 EOF
569
570     return split("\n", $getterBody);
571 }
572
573 sub generateBackendDispatcher
574 {
575     my @body;
576     my @mapEntries = map("        dispatchMap.add(${_}Cmd, &${backendClassName}::$_);", map ($backendMethodSignatures{$_}, @backendMethods));
577     my $mapEntries = join("\n", @mapEntries);
578
579     my $backendDispatcherBody = << "EOF";
580 void ${backendClassName}::dispatch(const String& message)
581 {
582     typedef void (${backendClassName}::*CallHandler)(long callId, InspectorObject* messageObject);
583     typedef HashMap<String, CallHandler> DispatchMap;
584     DEFINE_STATIC_LOCAL(DispatchMap, dispatchMap, );
585     long callId = 0;
586
587     if (dispatchMap.isEmpty()) {
588 $mapEntries
589     }
590
591     RefPtr<InspectorValue> parsedMessage = InspectorValue::parseJSON(message);
592     if (!parsedMessage) {
593         reportProtocolError(0, ParseError, "Message should be in JSON format.");
594         return;
595     }
596
597     RefPtr<InspectorObject> messageObject = parsedMessage->asObject();
598     if (!messageObject) {
599         reportProtocolError(0, InvalidRequest, "Invalid message format. The message should be a JSONified object.");
600         return;
601     }
602
603     RefPtr<InspectorValue> callIdValue = messageObject->get("id");
604     if (!callIdValue) {
605         reportProtocolError(0, InvalidRequest, "Invalid message format. 'id' property was not found in the request.");
606         return;
607     }
608
609     if (!callIdValue->asNumber(&callId)) {
610         reportProtocolError(0, InvalidRequest, "Invalid message format. The type of 'id' property should be number.");
611         return;
612     }
613
614     RefPtr<InspectorValue> methodValue = messageObject->get("method");
615     if (!methodValue) {
616         reportProtocolError(&callId, InvalidRequest, "Invalid message format. 'method' property wasn't found.");
617         return;
618     }
619
620     String method;
621     if (!methodValue->asString(&method)) {
622         reportProtocolError(&callId, InvalidRequest, "Invalid message format. The type of 'method' property should be string.");
623         return;
624     }
625
626     HashMap<String, CallHandler>::iterator it = dispatchMap.find(method);
627     if (it == dispatchMap.end()) {
628         reportProtocolError(&callId, MethodNotFound, makeString("Invalid method name was received. '", method, "' wasn't found."));
629         return;
630     }
631
632     ((*this).*it->second)(callId, messageObject.get());
633 }
634 EOF
635     return split("\n", $backendDispatcherBody);
636 }
637
638 sub generateBackendMessageParser
639 {
640     my $messageParserBody = << "EOF";
641 bool ${backendClassName}::getCommandName(const String& message, String* result)
642 {
643     RefPtr<InspectorValue> value = InspectorValue::parseJSON(message);
644     if (!value)
645         return false;
646
647     RefPtr<InspectorObject> object = value->asObject();
648     if (!object)
649         return false;
650
651     if (!object->getString("method", result))
652         return false;
653
654     return true;
655 }
656 EOF
657     return split("\n", $messageParserBody);
658 }
659
660 sub collectBackendJSStubFunctions
661 {
662     my $interface = shift;
663     my @functions = grep(!$_->signature->extendedAttributes->{"event"}, @{$interface->functions});
664     my $domain = $interface->name;
665
666     foreach my $function (@functions) {
667         my $name = $function->signature->name;
668         my @inArgs = grep($_->direction eq "in", @{$function->parameters});
669         my $argumentNames = join(
670             ",",
671             map("\"" . $_->name . "\": {"
672                 . "\"optional\": " . ($_->extendedAttributes->{"optional"} ? "true " : "false") . ", "
673                 . "\"type\": \"" . typeTraits($_->type, "JSType") . "\""
674                 . "}",
675                  @inArgs));
676         push(@backendJSStubs, "    this._registerDelegate('{" .
677             "\"method\": \"$domain.$name\", " .
678             (scalar(@inArgs) ? "\"params\": {$argumentNames}, " : "") .
679             "\"id\": 0" .
680         "}');");
681     }
682 }
683
684 sub generateBackendStubJS
685 {
686     my $JSStubs = join("\n", @backendJSStubs);
687     my $inspectorBackendStubJS = << "EOF";
688 $licenseTemplate
689
690 InspectorBackendStub = function()
691 {
692     this._lastCallbackId = 1;
693     this._pendingResponsesCount = 0;
694     this._callbacks = {};
695     this._domainDispatchers = {};
696 $JSStubs
697 }
698
699 InspectorBackendStub.prototype = {
700     _wrap: function(callback)
701     {
702         var callbackId = this._lastCallbackId++;
703         this._callbacks[callbackId] = callback || function() {};
704         return callbackId;
705     },
706
707     _registerDelegate: function(requestString)
708     {
709         var domainAndFunction = JSON.parse(requestString).method.split(".");
710         var agentName = domainAndFunction[0] + "Agent";
711         if (!window[agentName])
712             window[agentName] = {};
713         window[agentName][domainAndFunction[1]] = this.sendMessageToBackend.bind(this, requestString);
714     },
715
716     sendMessageToBackend: function()
717     {
718         var args = Array.prototype.slice.call(arguments);
719         var request = JSON.parse(args.shift());
720         var callback = (args.length && typeof args[args.length - 1] === "function") ? args.pop() : 0;
721         var domainAndMethod = request.method.split(".");
722         var agentMethod = domainAndMethod[0] + "Agent." + domainAndMethod[1];
723
724         if (request.params) {
725             for (var key in request.params) {
726                 var typeName = request.params[key].type;
727                 var optionalFlag = request.params[key].optional;
728
729                 if (args.length === 0 && !optionalFlag) {
730                     console.error("Protocol Error: Invalid number of arguments for method '" + agentMethod + "' call. It should have the next arguments '" + JSON.stringify(request.params) + "'.");
731                     return;
732                 }
733
734                 var value = args.shift();
735                 if (optionalFlag && typeof value === "undefined") {
736                     delete request.params[key];
737                     continue;
738                 }
739
740                 if (typeof value !== typeName) {
741                     console.error("Protocol Error: Invalid type of argument '" + key + "' for method '" + agentMethod + "' call. It should be '" + typeName + "' but it is '" + typeof value + "'.");
742                     return;
743                 }
744
745                 request.params[key] = value;
746             }
747         }
748
749         if (args.length === 1 && !callback) {
750             if (typeof args[0] !== "undefined") {
751                 console.error("Protocol Error: Optional callback argument for method '" + agentMethod + "' call should be a function but its type is '" + typeof args[0] + "'.");
752                 return;
753             }
754         }
755         request.id = this._wrap(callback || function() {});
756
757         if (window.dumpInspectorProtocolMessages)
758             console.log("frontend: " + JSON.stringify(request));
759
760         var message = JSON.stringify(request);
761
762         ++this._pendingResponsesCount;
763         InspectorFrontendHost.sendMessageToBackend(message);
764     },
765
766     registerDomainDispatcher: function(domain, dispatcher)
767     {
768         this._domainDispatchers[domain] = dispatcher;
769     },
770
771     dispatch: function(message)
772     {
773         if (window.dumpInspectorProtocolMessages)
774             console.log("backend: " + ((typeof message === "string") ? message : JSON.stringify(message)));
775
776         var messageObject = (typeof message === "string") ? JSON.parse(message) : message;
777
778         if ("id" in messageObject) { // just a response for some request
779             if (messageObject.error && messageObject.error.code !== -32000)
780                 this.reportProtocolError(messageObject);
781
782             var arguments = [];
783             if (messageObject.result) {
784                 for (var key in messageObject.result)
785                     arguments.push(messageObject.result[key]);
786             }
787
788             var callback = this._callbacks[messageObject.id];
789             if (callback) {
790                 arguments.unshift(messageObject.error);
791                 callback.apply(null, arguments);
792                 --this._pendingResponsesCount;
793                 delete this._callbacks[messageObject.id];
794             }
795
796             if (this._scripts && !this._pendingResponsesCount)
797                 this.runAfterPendingDispatches();
798
799             return;
800         } else {
801             var method = messageObject.method.split(".");
802             var domainName = method[0];
803             var functionName = method[1];
804             if (!(domainName in this._domainDispatchers)) {
805                 console.error("Protocol Error: the message is for non-existing domain '" + domainName + "'");
806                 return;
807             }
808             var dispatcher = this._domainDispatchers[domainName];
809             if (!(functionName in dispatcher)) {
810                 console.error("Protocol Error: Attempted to dispatch an unimplemented method '" + messageObject.method + "'");
811                 return;
812             }
813
814             var params = [];
815             if (messageObject.params) {
816                 for (var key in messageObject.params)
817                     params.push(messageObject.params[key]);
818             }
819
820             dispatcher[functionName].apply(dispatcher, params);
821         }
822     },
823
824     reportProtocolError: function(messageObject)
825     {
826         var error = messageObject.error;
827         console.error(error.message + "(" + error.code + "): request with id = " + messageObject.id + " failed.");
828         for (var i = 0; i < error.data.length; ++i)
829             console.error("    " + error.data[i]);
830     },
831
832     runAfterPendingDispatches: function(script)
833     {
834         if (!this._scripts)
835             this._scripts = [];
836
837         if (script)
838             this._scripts.push(script);
839
840         if (!this._pendingResponsesCount) {
841             var scripts = this._scripts;
842             this._scripts = []
843             for (var id = 0; id < scripts.length; ++id)
844                  scripts[id].call(this);
845         }
846     }
847 }
848
849 InspectorBackend = new InspectorBackendStub();
850
851 EOF
852     return split("\n", $inspectorBackendStubJS);
853 }
854
855 sub generateHeader
856 {
857     my $className = shift;
858     my $types = shift;
859     my $constructor = shift;
860     my $constants = shift;
861     my $methods = shift;
862     my $footer = shift;
863
864     my $forwardHeaders = join("\n", sort(map("#include <" . typeTraits($_, "forwardHeader") . ">", grep(typeTraits($_, "forwardHeader"), keys %{$types}))));
865     my $forwardDeclarations = join("\n", sort(map("class " . typeTraits($_, "forward") . ";", grep(typeTraits($_, "forward"), keys %{$types}))));
866     my $constantDeclarations = join("\n", @{$constants});
867     my $methodsDeclarations = join("\n", @{$methods});
868
869     my $headerBody = << "EOF";
870 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
871 // Use of this source code is governed by a BSD-style license that can be
872 // found in the LICENSE file.
873 #ifndef ${className}_h
874 #define ${className}_h
875
876 ${forwardHeaders}
877
878 namespace $namespace {
879
880 $forwardDeclarations
881
882 typedef String ErrorString;
883
884 class $className {
885 public:
886 $constructor
887
888 $constantDeclarations
889 $methodsDeclarations
890
891 $footer
892 };
893
894 } // namespace $namespace
895 #endif // !defined(${className}_h)
896
897 EOF
898     return $headerBody;
899 }
900
901 sub generateSource
902 {
903     my $className = shift;
904     my $types = shift;
905     my $constants = shift;
906     my $methods = shift;
907
908     my @sourceContent = split("\r", $licenseTemplate);
909     push(@sourceContent, "\n#include \"config.h\"");
910     push(@sourceContent, "#include \"$className.h\"");
911     push(@sourceContent, "#include <wtf/text/StringConcatenate.h>");
912     push(@sourceContent, "#include <wtf/text/CString.h>");
913     push(@sourceContent, "");
914     push(@sourceContent, "#if ENABLE(INSPECTOR)");
915     push(@sourceContent, "");
916
917     my %headers;
918     foreach my $type (keys %{$types}) {
919         $headers{"#include \"" . typeTraits($type, "header") . "\""} = 1 if !typeTraits($type, "header") eq  "";
920     }
921     push(@sourceContent, sort keys %headers);
922     push(@sourceContent, "");
923     push(@sourceContent, "namespace $namespace {");
924     push(@sourceContent, "");
925     push(@sourceContent, join("\n", @{$constants}));
926     push(@sourceContent, "");
927     push(@sourceContent, @{$methods});
928     push(@sourceContent, "");
929     push(@sourceContent, "} // namespace $namespace");
930     push(@sourceContent, "");
931     push(@sourceContent, "#endif // ENABLE(INSPECTOR)");
932     push(@sourceContent, "");
933     return @sourceContent;
934 }
935
936 sub typeTraits
937 {
938     my $type = shift;
939     my $trait = shift;
940     return $typeTransform{$type}->{$trait};
941 }
942
943 sub generateBackendAgentFieldsAndConstructor
944 {
945     my @arguments;
946     my @fieldInitializers;
947
948     push(@arguments, "InspectorFrontendChannel* inspectorFrontendChannel");
949     push(@fieldInitializers, "        : m_inspectorFrontendChannel(inspectorFrontendChannel)");
950     push(@backendFooter, "    InspectorFrontendChannel* m_inspectorFrontendChannel;");
951
952     foreach my $domain (sort keys %backendDomains) {
953         # Add agent field declaration to the footer.
954         my $agentClassName = typeTraits($domain, "forward");
955         my $field = typeTraits($domain, "domainAccessor");
956         push(@backendFooter, "    ${agentClassName}* ${field};");
957
958         # Add agent parameter and initializer.
959         my $arg = substr($field, 2);
960         push(@fieldInitializers, "        , ${field}(${arg})");
961         push(@arguments, "${agentClassName}* ${arg}");
962     }
963
964     my $argumentString = join(", ", @arguments);
965
966     my @backendHead;
967     push(@backendHead, "    ${backendClassName}(${argumentString})");
968     push(@backendHead, @fieldInitializers);
969     push(@backendHead, "    { }");
970     push(@backendHead, "");
971     push(@backendHead, "    enum CommonErrorCode {");
972     push(@backendHead, "        ParseError = 0,");
973     push(@backendHead, "        InvalidRequest,");
974     push(@backendHead, "        MethodNotFound,");
975     push(@backendHead, "        InvalidParams,");
976     push(@backendHead, "        InternalError,");
977     push(@backendHead, "        ServerError,");
978     push(@backendHead, "        LastEntry,");
979     push(@backendHead, "    };");
980     push(@backendHead, "");
981     push(@backendHead, "    void reportProtocolError(const long* const callId, CommonErrorCode, const String& errorText) const;");
982     push(@backendHead, "    void reportProtocolError(const long* const callId, CommonErrorCode, PassRefPtr<InspectorArray> data) const;");
983     push(@backendHead, "    void dispatch(const String& message);");
984     push(@backendHead, "    static bool getCommandName(const String& message, String* result);");
985     $backendConstructor = join("\n", @backendHead);
986 }
987
988 sub finish
989 {
990     my $object = shift;
991
992     push(@backendMethodsImpl, generateBackendDispatcher());
993     push(@backendMethodsImpl, generateBackendReportProtocolError());
994     unshift(@frontendMethodsImpl, generateFrontendConstructorImpl(), "");
995
996     open(my $SOURCE, ">$outputDir/$frontendClassName.cpp") || die "Couldn't open file $outputDir/$frontendClassName.cpp";
997     print $SOURCE join("\n", generateSource($frontendClassName, \%frontendTypes, \@frontendConstantDefinitions, \@frontendMethodsImpl));
998     close($SOURCE);
999     undef($SOURCE);
1000
1001     open(my $HEADER, ">$outputHeadersDir/$frontendClassName.h") || die "Couldn't open file $outputHeadersDir/$frontendClassName.h";
1002     print $HEADER generateHeader($frontendClassName, \%frontendTypes, $frontendConstructor, \@frontendConstantDeclarations, \@frontendMethods, join("\n", @frontendFooter));
1003     close($HEADER);
1004     undef($HEADER);
1005
1006     # Make dispatcher methods private on the backend.
1007     push(@backendConstantDeclarations, "");
1008     push(@backendConstantDeclarations, "private:");
1009
1010     foreach my $type (keys %backendTypes) {
1011         if (typeTraits($type, "JSONType")) {
1012             push(@backendMethodsImpl, generateArgumentGetters($type));
1013         }
1014     }
1015     generateBackendAgentFieldsAndConstructor();
1016
1017     push(@backendMethodsImpl, generateBackendMessageParser());
1018     push(@backendMethodsImpl, "");
1019
1020     push(@backendConstantDeclarations, "");
1021
1022     open($SOURCE, ">$outputDir/$backendClassName.cpp") || die "Couldn't open file $outputDir/$backendClassName.cpp";
1023     print $SOURCE join("\n", generateSource($backendClassName, \%backendTypes, \@backendConstantDefinitions, \@backendMethodsImpl));
1024     close($SOURCE);
1025     undef($SOURCE);
1026
1027     open($HEADER, ">$outputHeadersDir/$backendClassName.h") || die "Couldn't open file $outputHeadersDir/$backendClassName.h";
1028     print $HEADER join("\n", generateHeader($backendClassName, \%backendTypes, $backendConstructor, \@backendConstantDeclarations, \@backendMethods, join("\n", @backendFooter)));
1029     close($HEADER);
1030     undef($HEADER);
1031
1032     open(my $JS_STUB, ">$outputDir/$backendJSStubName.js") || die "Couldn't open file $outputDir/$backendJSStubName.js";
1033     print $JS_STUB join("\n", generateBackendStubJS());
1034     close($JS_STUB);
1035     undef($JS_STUB);
1036 }
1037
1038 1;