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