2011-01-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_inspectorController->m_applicationCacheAgent",
17 };
18 $typeTransform{"CSS"} = {
19     "forward" => "InspectorCSSAgent",
20     "header" => "InspectorCSSAgent.h",
21     "domainAccessor" => "m_inspectorController->m_cssAgent",
22 };
23 $typeTransform{"Debugger"} = {
24     "forward" => "InspectorDebuggerAgent",
25     "header" => "InspectorDebuggerAgent.h",
26     "domainAccessor" => "m_inspectorController->m_debuggerAgent",
27 };
28 $typeTransform{"BrowserDebugger"} = {
29     "forward" => "InspectorBrowserDebuggerAgent",
30     "header" => "InspectorBrowserDebuggerAgent.h",
31     "domainAccessor" => "m_inspectorController->m_browserDebuggerAgent",
32 };
33 $typeTransform{"Database"} = {
34     "forward" => "InspectorDatabaseAgent",
35     "header" => "InspectorDatabaseAgent.h",
36     "domainAccessor" => "m_inspectorController->m_databaseAgent",
37 };
38 $typeTransform{"DOM"} = {
39     "forward" => "InspectorDOMAgent",
40     "header" => "InspectorDOMAgent.h",
41     "domainAccessor" => "m_inspectorController->m_domAgent",
42 };
43 $typeTransform{"DOMStorage"} = {
44     "forward" => "InspectorDOMStorageAgent",
45     "header" => "InspectorDOMStorageAgent.h",
46     "domainAccessor" => "m_inspectorController->m_domStorageAgent",
47 };
48 $typeTransform{"FileSystem"} = {
49     "forward" => "InspectorFileSystemAgent",
50     "header" => "InspectorFileSystemAgent.h",
51     "domainAccessor" => "m_inspectorController->m_fileSystemAgent",
52 };
53 $typeTransform{"Inspector"} = {
54     "forwardHeader" => "InspectorController.h",
55     "domainAccessor" => "m_inspectorController",
56 };
57 $typeTransform{"Network"} = {
58     "forward" => "InspectorResourceAgent",
59     "header" => "InspectorResourceAgent.h",
60     "domainAccessor" => "m_inspectorController->m_resourceAgent",
61 };
62 $typeTransform{"Profiler"} = {
63     "forward" => "InspectorProfilerAgent",
64     "header" => "InspectorProfilerAgent.h",
65     "domainAccessor" => "m_inspectorController->m_profilerAgent",
66 };
67
68 $typeTransform{"Frontend"} = {
69     "forward" => "InspectorFrontend",
70     "header" => "InspectorFrontend.h",
71 };
72 $typeTransform{"InspectorClient"} = {
73     "forward" => "InspectorClient",
74     "header" => "InspectorClient.h",
75 };
76 $typeTransform{"PassRefPtr"} = {
77     "forwardHeader" => "wtf/PassRefPtr.h",
78 };
79 $typeTransform{"Object"} = {
80     "param" => "PassRefPtr<InspectorObject>",
81     "variable" => "RefPtr<InspectorObject>",
82     "defaultValue" => "InspectorObject::create()",
83     "forward" => "InspectorObject",
84     "header" => "InspectorValues.h",
85     "JSONType" => "Object"
86 };
87 $typeTransform{"Array"} = {
88     "param" => "PassRefPtr<InspectorArray>",
89     "variable" => "RefPtr<InspectorArray>",
90     "defaultValue" => "InspectorArray::create()",
91     "forward" => "InspectorArray",
92     "header" => "InspectorValues.h",
93     "JSONType" => "Array"
94 };
95 $typeTransform{"Value"} = {
96     "param" => "PassRefPtr<InspectorValue>",
97     "variable" => "RefPtr<InspectorValue>",
98     "defaultValue" => "InspectorValue::null()",
99     "forward" => "InspectorValue",
100     "header" => "InspectorValues.h",
101     "JSONType" => "Value"
102 };
103 $typeTransform{"String"} = {
104     "param" => "const String&",
105     "variable" => "String",
106     "return" => "String",
107     "defaultValue" => "\"\"",
108     "forwardHeader" => "wtf/Forward.h",
109     "header" => "PlatformString.h",
110     "JSONType" => "String"
111 };
112 $typeTransform{"long"} = {
113     "param" => "long",
114     "variable" => "long",
115     "defaultValue" => "0",
116     "forward" => "",
117     "header" => "",
118     "JSONType" => "Number"
119 };
120 $typeTransform{"int"} = {
121     "param" => "int",
122     "variable" => "int",
123     "defaultValue" => "0",
124     "forward" => "",
125     "header" => "",
126     "JSONType" => "Number",
127 };
128 $typeTransform{"unsigned long"} = {
129     "param" => "unsigned long",
130     "variable" => "unsigned long",
131     "defaultValue" => "0u",
132     "forward" => "",
133     "header" => "",
134     "JSONType" => "Number"
135 };
136 $typeTransform{"unsigned int"} = {
137     "param" => "unsigned int",
138     "variable" => "unsigned int",
139     "defaultValue" => "0u",
140     "forward" => "",
141     "header" => "",
142     "JSONType" => "Number"
143 };
144 $typeTransform{"double"} = {
145     "param" => "double",
146     "variable" => "double",
147     "defaultValue" => "0.0",
148     "forward" => "",
149     "header" => "",
150     "JSONType" => "Number"
151 };
152 $typeTransform{"boolean"} = {
153     "param" => "bool",
154     "variable"=> "bool",
155     "defaultValue" => "false",
156     "forward" => "",
157     "header" => "",
158     "JSONType" => "Boolean"
159 };
160 $typeTransform{"void"} = {
161     "forward" => "",
162     "header" => ""
163 };
164
165 # Default License Templates
166
167 my $licenseTemplate = << "EOF";
168 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
169 // Use of this source code is governed by a BSD-style license that can be
170 // found in the LICENSE file.
171 EOF
172
173 my $codeGenerator;
174 my $outputDir;
175 my $outputHeadersDir;
176 my $writeDependencies;
177 my $verbose;
178
179 my $namespace;
180
181 my $backendClassName;
182 my $backendJSStubName;
183 my %backendTypes;
184 my %backendMethods;
185 my @backendMethodsImpl;
186 my $backendConstructor;
187 my @backendConstantDeclarations;
188 my @backendConstantDefinitions;
189 my $backendFooter;
190 my @backendStubJS;
191
192 my $frontendClassName;
193 my %frontendTypes;
194 my %frontendMethods;
195 my @frontendMethodsImpl;
196 my $frontendConstructor;
197 my @frontendConstantDeclarations;
198 my @frontendConstantDefinitions;
199 my $frontendFooter;
200
201 # Default constructor
202 sub new
203 {
204     my $object = shift;
205     my $reference = { };
206
207     $codeGenerator = shift;
208     $outputDir = shift;
209     $outputHeadersDir = shift;
210     shift; # $useLayerOnTop
211     shift; # $preprocessor
212     $writeDependencies = shift;
213     $verbose = shift;
214
215     bless($reference, $object);
216     return $reference;
217 }
218
219 # Params: 'idlDocument' struct
220 sub GenerateModule
221 {
222     my $object = shift;
223     my $dataNode = shift;
224
225     $namespace = $dataNode->module;
226     $namespace =~ s/core/WebCore/;
227 }
228
229 # Params: 'idlDocument' struct
230 sub GenerateInterface
231 {
232     my $object = shift;
233     my $interface = shift;
234     my $defines = shift;
235
236     my $className = $interface->name;
237
238     $frontendClassName = $className . "Frontend";
239     $frontendConstructor = "    ${frontendClassName}(InspectorClient* inspectorClient) : m_inspectorClient(inspectorClient) { }";
240     $frontendFooter = "    InspectorClient* m_inspectorClient;";
241     $frontendTypes{"String"} = 1;
242     $frontendTypes{"InspectorClient"} = 1;
243     $frontendTypes{"PassRefPtr"} = 1;
244
245     $backendClassName = $className . "BackendDispatcher";
246     $backendJSStubName = $className . "BackendStub";
247     my @backendHead;
248     push(@backendHead, "    ${backendClassName}(InspectorController* inspectorController) : m_inspectorController(inspectorController) { }");
249     push(@backendHead, "    void reportProtocolError(const long callId, const String& errorText) const;");
250     push(@backendHead, "    void dispatch(const String& message);");
251     push(@backendHead, "    static bool getCommandName(const String& message, String* result);");
252     $backendConstructor = join("\n", @backendHead);
253     $backendFooter = "    InspectorController* m_inspectorController;";
254     $backendTypes{"Inspector"} = 1;
255     $backendTypes{"InspectorClient"} = 1;
256     $backendTypes{"PassRefPtr"} = 1;
257     $backendTypes{"Object"} = 1;
258
259     push(@backendMethodsImpl, generateBackendMessageParser());
260     generateFunctions($interface);
261
262     # Make dispatcher methods private on the backend.
263     push(@backendConstantDeclarations, "");
264     push(@backendConstantDeclarations, "private:");
265 }
266
267 sub generateFunctions
268 {
269     my $interface = shift;
270
271     foreach my $function (@{$interface->functions}) {
272         if ($function->signature->extendedAttributes->{"notify"}) {
273             generateFrontendFunction($function);
274         } else {
275             generateBackendFunction($function);
276         }
277     }
278     push(@backendMethodsImpl, generateBackendDispatcher());
279     push(@backendMethodsImpl, generateBackendReportProtocolError());
280
281     foreach my $type (keys %backendTypes) {
282         if ($typeTransform{$type}{"JSONType"}) {
283             push(@backendMethodsImpl, generateArgumentGetters($type));
284         }
285     }
286
287     @backendStubJS = generateBackendStubJS($interface);
288 }
289
290 sub generateFrontendFunction
291 {
292     my $function = shift;
293
294     my $functionName = $function->signature->name;
295
296     my $domain = $function->signature->extendedAttributes->{"domain"} || "Inspector";
297     my @argsFiltered = grep($_->direction eq "out", @{$function->parameters}); # just keep only out parameters for frontend interface.
298     map($frontendTypes{$_->type} = 1, @argsFiltered); # register required types.
299     my $arguments = join(", ", map($typeTransform{$_->type}->{"param"} . " " . $_->name, @argsFiltered)); # prepare arguments for function signature.
300
301     my $signature = "    void ${functionName}(${arguments});";
302     if (!$frontendMethods{${signature}}) {
303         $frontendMethods{${signature}} = 1;
304
305         my @function;
306         push(@function, "void ${frontendClassName}::${functionName}(${arguments})");
307         push(@function, "{");
308         push(@function, "    RefPtr<InspectorObject> ${functionName}Message = InspectorObject::create();");
309         push(@function, "    ${functionName}Message->setString(\"type\", \"event\");");
310         push(@function, "    ${functionName}Message->setString(\"domain\", \"$domain\");");
311         push(@function, "    ${functionName}Message->setString(\"event\", \"$functionName\");");
312         push(@function, "    RefPtr<InspectorObject> payloadDataObject = InspectorObject::create();");
313         my @pushArguments = map("    payloadDataObject->set" . $typeTransform{$_->type}->{"JSONType"} . "(\"" . $_->name . "\", " . $_->name . ");", @argsFiltered);
314         push(@function, @pushArguments);
315         push(@function, "    ${functionName}Message->setObject(\"data\", payloadDataObject);");
316         push(@function, "    m_inspectorClient->sendMessageToFrontend(${functionName}Message->toJSONString());");
317
318         push(@function, "}");
319         push(@function, "");
320         push(@frontendMethodsImpl, @function);
321     }
322 }
323
324 sub camelCase
325 {
326     my $value = shift;
327     $value =~ s/\b(\w)/\U$1/g; # make a camel-case name for type name
328     $value =~ s/ //g;
329     return $value;
330 }
331
332 sub generateBackendFunction
333 {
334     my $function = shift;
335
336     my $functionName = $function->signature->name;
337
338     push(@backendConstantDeclarations, "    static const char* ${functionName}Cmd;");
339     push(@backendConstantDefinitions, "const char* ${backendClassName}::${functionName}Cmd = \"${functionName}\";");
340
341     map($backendTypes{$_->type} = 1, @{$function->parameters}); # register required types
342     my @inArgs = grep($_->direction eq "in" && !($_->name eq "callId") , @{$function->parameters});
343     my @outArgs = grep($_->direction eq "out", @{$function->parameters});
344
345     my $signature = "    void ${functionName}(long callId, InspectorObject* requestMessageObject);";
346     !$backendMethods{${signature}} || die "Duplicate function was detected for signature '$signature'.";
347     $backendMethods{${signature}} = $functionName;
348
349     my @function;
350     my $requestMessageObject = scalar(@inArgs) ? " requestMessageObject" : "";
351     push(@function, "void ${backendClassName}::${functionName}(long callId, InspectorObject*$requestMessageObject)");
352     push(@function, "{");
353     push(@function, "    RefPtr<InspectorArray> protocolErrors = InspectorArray::create();");
354     push(@function, "");
355
356     my $domain = $function->signature->extendedAttributes->{"domain"} || "Inspector";
357     my $domainAccessor = $typeTransform{$domain}->{"domainAccessor"};
358     $backendTypes{$domain} = 1;
359     push(@function, "    if (!$domainAccessor)");
360     push(@function, "        protocolErrors->pushString(\"Protocol Error: $domain handler is not available.\");");
361     push(@function, "");
362
363     # declare local variables for out arguments.
364     push(@function, map("    " . $typeTransform{$_->type}->{"variable"} . " " . $_->name . " = " . $typeTransform{$_->type}->{"defaultValue"} . ";", @outArgs));
365
366     my $indent = "";
367     if (scalar(@inArgs)) {
368         push(@function, "    if (RefPtr<InspectorObject> argumentsContainer = requestMessageObject->getObject(\"arguments\")) {");
369
370         foreach my $parameter (@inArgs) {
371             my $name = $parameter->name;
372             my $type = $parameter->type;
373             my $typeString = camelCase($parameter->type);
374             push(@function, "        " . $typeTransform{$type}->{"variable"} . " $name = get$typeString(argumentsContainer.get(), \"$name\", protocolErrors.get());");
375         }
376         push(@function, "");
377         $indent = "    ";
378     }
379
380     my $args = join(", ", (map($_->name, @inArgs), map("&" . $_->name, @outArgs)));
381     push(@function, "$indent    if (!protocolErrors->length())");
382     push(@function, "$indent        $domainAccessor->$functionName($args);");
383     if (scalar(@inArgs)) {
384         push(@function, "    } else {");
385         push(@function, "        protocolErrors->pushString(\"Protocol Error: 'arguments' property with type 'object' was not found.\");");
386         push(@function, "    }");
387     }
388
389     push(@function, "    // use InspectorFrontend as a marker of WebInspector availability");
390     push(@function, "    if ((callId || protocolErrors->length()) && m_inspectorController->hasFrontend()) {");
391     push(@function, "        RefPtr<InspectorObject> responseMessage = InspectorObject::create();");
392     push(@function, "        responseMessage->setNumber(\"seq\", callId);");
393     push(@function, "        responseMessage->setString(\"domain\", \"$domain\");");
394     push(@function, "        responseMessage->setBoolean(\"success\", !protocolErrors->length());");
395     push(@function, "");
396     push(@function, "        if (protocolErrors->length())");
397     push(@function, "            responseMessage->setArray(\"errors\", protocolErrors);");
398     if (scalar(@outArgs)) {
399         push(@function, "        else {");
400         push(@function, "            RefPtr<InspectorObject> responseData = InspectorObject::create();");
401         push(@function, map("            responseData->set" . $typeTransform{$_->type}->{"JSONType"} . "(\"" . $_->name . "\", " . $_->name . ");", @outArgs));
402         push(@function, "            responseMessage->setObject(\"data\", responseData);");
403         push(@function, "        }");
404     }
405     push(@function, "        m_inspectorController->inspectorClient()->sendMessageToFrontend(responseMessage->toJSONString());");
406     push(@function, "    }");
407
408
409     push(@function, "}");
410     push(@function, "");
411     push(@backendMethodsImpl, @function);
412 }
413
414 sub generateBackendReportProtocolError
415 {
416     my $reportProtocolError = << "EOF";
417
418 void ${backendClassName}::reportProtocolError(const long callId, const String& errorText) const
419 {
420     RefPtr<InspectorObject> message = InspectorObject::create();
421     message->setNumber("seq", callId);
422     message->setBoolean("success", false);
423     RefPtr<InspectorArray> errors = InspectorArray::create();
424     errors->pushString(errorText);
425     message->setArray("errors", errors);
426     m_inspectorController->inspectorClient()->sendMessageToFrontend(message->toJSONString());
427 }
428 EOF
429     return split("\n", $reportProtocolError);
430 }
431
432 sub generateArgumentGetters
433 {
434     my $type = shift;
435     my $json = $typeTransform{$type}{"JSONType"};
436     my $variable = $typeTransform{$type}{"variable"};
437     my $defaultValue = $typeTransform{$type}{"defaultValue"};
438     my $return  = $typeTransform{$type}{"return"} ? $typeTransform{$type}{"return"} : $typeTransform{$type}{"param"};
439
440     my $typeString = camelCase($type);
441     push(@backendConstantDeclarations, "$return get$typeString(InspectorObject* object, const String& name, InspectorArray* protocolErrors);");
442     my $getterBody = << "EOF";
443
444 $return InspectorBackendDispatcher::get$typeString(InspectorObject* object, const String& name, InspectorArray* protocolErrors)
445 {
446     ASSERT(object);
447     ASSERT(protocolErrors);
448
449     $variable value = $defaultValue;
450     InspectorObject::const_iterator end = object->end();
451     InspectorObject::const_iterator valueIterator = object->find(name);
452
453     if (valueIterator == end)
454         protocolErrors->pushString(String::format("Protocol Error: Argument '\%s' with type '$json' was not found.", name.utf8().data()));
455     else {
456         if (!valueIterator->second->as$json(&value))
457             protocolErrors->pushString(String::format("Protocol Error: Argument '\%s' has wrong type. It should be '$json'.", name.utf8().data()));
458     }
459     return value;
460 }
461 EOF
462
463     return split("\n", $getterBody);
464 }
465
466 sub generateBackendDispatcher
467 {
468     my @body;
469     my @methods = map($backendMethods{$_}, keys %backendMethods);
470     my @mapEntries = map("        dispatchMap.add(${_}Cmd, &${backendClassName}::$_);", @methods);
471     my $mapEntries = join("\n", @mapEntries);
472
473     my $backendDispatcherBody = << "EOF";
474 void ${backendClassName}::dispatch(const String& message)
475 {
476     typedef void (${backendClassName}::*CallHandler)(long callId, InspectorObject* messageObject);
477     typedef HashMap<String, CallHandler> DispatchMap;
478     DEFINE_STATIC_LOCAL(DispatchMap, dispatchMap, );
479     long callId = 0;
480
481     if (dispatchMap.isEmpty()) {
482 $mapEntries
483     }
484
485     RefPtr<InspectorValue> parsedMessage = InspectorValue::parseJSON(message);
486     if (!parsedMessage) {
487         reportProtocolError(callId, "Protocol Error: Invalid message format. Message should be in JSON format.");
488         return;
489     }
490
491     RefPtr<InspectorObject> messageObject = parsedMessage->asObject();
492     if (!messageObject) {
493         reportProtocolError(callId, "Protocol Error: Invalid message format. The message should be a JSONified object.");
494         return;
495     }
496
497     RefPtr<InspectorValue> commandValue = messageObject->get("command");
498     if (!commandValue) {
499         reportProtocolError(callId, "Protocol Error: Invalid message format. 'command' property wasn't found.");
500         return;
501     }
502
503     String command;
504     if (!commandValue->asString(&command)) {
505         reportProtocolError(callId, "Protocol Error: Invalid message format. The type of 'command' property should be string.");
506         return;
507     }
508
509     RefPtr<InspectorValue> callIdValue = messageObject->get("seq");
510     if (!callIdValue) {
511         reportProtocolError(callId, "Protocol Error: Invalid message format. 'seq' property was not found in the request.");
512         return;
513     }
514
515     if (!callIdValue->asNumber(&callId)) {
516         reportProtocolError(callId, "Protocol Error: Invalid message format. The type of 'seq' property should be number.");
517         return;
518     }
519
520     HashMap<String, CallHandler>::iterator it = dispatchMap.find(command);
521     if (it == dispatchMap.end()) {
522         reportProtocolError(callId, makeString("Protocol Error: Invalid command was received. '", command, "' wasn't found."));
523         return;
524     }
525
526     ((*this).*it->second)(callId, messageObject.get());
527 }
528 EOF
529     return split("\n", $backendDispatcherBody);
530 }
531
532 sub generateBackendMessageParser
533 {
534     my $messageParserBody = << "EOF";
535 bool ${backendClassName}::getCommandName(const String& message, String* result)
536 {
537     RefPtr<InspectorValue> value = InspectorValue::parseJSON(message);
538     if (!value)
539         return false;
540
541     RefPtr<InspectorObject> object = value->asObject();
542     if (!object)
543         return false;
544
545     RefPtr<InspectorValue> commandValue = object->get("command");
546     if (!commandValue)
547         return false;
548
549     return commandValue->asString(result);
550 }
551
552 EOF
553     return split("\n", $messageParserBody);
554 }
555
556 sub generateBackendStubJS
557 {
558     my $interface = shift;
559     my @backendFunctions = grep(!$_->signature->extendedAttributes->{"notify"}, @{$interface->functions});
560     my @JSStubs;
561
562     foreach my $function (@backendFunctions) {
563         my $name = $function->signature->name;
564         my $domain = $function->signature->extendedAttributes->{"domain"};
565         my $argumentNames = join(",", map("\"" . $_->name . "\": \"" . lc($typeTransform{$_->type}->{"JSONType"}) . "\"", grep($_->direction eq "in", @{$function->parameters})));
566         push(@JSStubs, "    this._registerDelegate('{" .
567             "\"seq\": 0, " .
568             "\"domain\": \"$domain\", " .
569             "\"command\": \"$name\", " .
570             "\"arguments\": {$argumentNames}" .
571         "}');");
572     }
573
574     my $JSStubs = join("\n", @JSStubs);
575     my $inspectorBackendStubJS = << "EOF";
576 $licenseTemplate
577
578 InspectorBackendStub = function()
579 {
580     this._lastCallbackId = 1;
581     this._callbacks = {};
582     this._domainDispatchers = {};
583 $JSStubs
584 }
585
586 InspectorBackendStub.prototype = {
587     _wrap: function(callback)
588     {
589         var callbackId = this._lastCallbackId++;
590         this._callbacks[callbackId] = callback || function() {};
591         return callbackId;
592     },
593
594     _processResponse: function(callbackId, args)
595     {
596         var callback = this._callbacks[callbackId];
597         callback.apply(null, args);
598         delete this._callbacks[callbackId];
599     },
600
601     _removeResponseCallbackEntry: function(callbackId)
602     {
603         delete this._callbacks[callbackId];
604     },
605
606     _registerDelegate: function(commandInfo)
607     {
608         var commandObject = JSON.parse(commandInfo);
609         this[commandObject.command] = this.sendMessageToBackend.bind(this, commandInfo);
610     },
611
612     sendMessageToBackend: function()
613     {
614         var args = Array.prototype.slice.call(arguments);
615         var request = JSON.parse(args.shift());
616
617         for (var key in request.arguments) {
618             if (args.length === 0) {
619                 console.error("Protocol Error: Invalid number of arguments for 'InspectorBackend.%s' call. It should have the next arguments '%s'.", request.command, JSON.stringify(request.arguments));
620                 return;
621             }
622             var value = args.shift();
623             if (typeof value !== request.arguments[key]) {
624                 console.error("Protocol Error: Invalid type of argument '%s' for 'InspectorBackend.%s' call. It should be '%s' but it is '%s'.", key, request.command, request.arguments[key], typeof value);
625                 return;
626             }
627             request.arguments[key] = value;
628         }
629
630         if (args.length === 1) {
631             if (typeof args[0] !== "function" && typeof args[0] !== "undefined") {
632                 console.error("Protocol Error: Optional callback argument for 'InspectorBackend.%s' call should be a function but its type is '%s'.", request.command, typeof args[0]);
633                 return;
634             }
635             request.seq = this._wrap(args[0]);
636         }
637
638         if (window.dumpInspectorProtocolMessages)
639             console.log("frontend: " + JSON.stringify(request));
640
641         var message = JSON.stringify(request);
642         InspectorFrontendHost.sendMessageToBackend(message);
643     },
644
645     registerDomainDispatcher: function(domain, dispatcher)
646     {
647         this._domainDispatchers[domain] = dispatcher;
648     },
649
650     dispatch: function(message)
651     {
652         if (window.dumpInspectorProtocolMessages)
653             console.log("backend: " + ((typeof message === "string") ? message : JSON.stringify(message)));
654
655         var messageObject = (typeof message === "string") ? JSON.parse(message) : message;
656
657         var arguments = [];
658         if (messageObject.data)
659             for (var key in messageObject.data)
660                 arguments.push(messageObject.data[key]);
661
662         if ("seq" in messageObject) { // just a response for some request
663             if (messageObject.success)
664                 this._processResponse(messageObject.seq, arguments);
665             else {
666                 this._removeResponseCallbackEntry(messageObject.seq)
667                 this.reportProtocolError(messageObject);
668             }
669             return;
670         }
671
672         if (messageObject.type === "event") {
673             if (!(messageObject.domain in this._domainDispatchers)) {
674                 console.error("Protocol Error: the message is for non-existing domain '%s'", messageObject.domain);
675                 return;
676             }
677             var dispatcher = this._domainDispatchers[messageObject.domain];
678             if (!(messageObject.event in dispatcher)) {
679                 console.error("Protocol Error: Attempted to dispatch an unimplemented method '%s.%s'", messageObject.domain, messageObject.event);
680                 return;
681             }
682             dispatcher[messageObject.event].apply(dispatcher, arguments);
683         }
684     },
685
686     reportProtocolError: function(messageObject)
687     {
688         console.error("Protocol Error: InspectorBackend request with seq = %d failed.", messageObject.seq);
689         for (var i = 0; i < messageObject.errors.length; ++i)
690             console.error("    " + messageObject.errors[i]);
691         this._removeResponseCallbackEntry(messageObject.seq);
692     }
693 }
694
695 InspectorBackend = new InspectorBackendStub();
696
697 EOF
698     return split("\n", $inspectorBackendStubJS);
699 }
700
701 sub generateHeader
702 {
703     my $className = shift;
704     my $types = shift;
705     my $constructor = shift;
706     my $constants = shift;
707     my $methods = shift;
708     my $footer = shift;
709
710     my $forwardHeaders = join("\n", sort(map("#include <" . $typeTransform{$_}->{"forwardHeader"} . ">", grep($typeTransform{$_}->{"forwardHeader"}, keys %{$types}))));
711     my $forwardDeclarations = join("\n", sort(map("class " . $typeTransform{$_}->{"forward"} . ";", grep($typeTransform{$_}->{"forward"}, keys %{$types}))));
712     my $constantDeclarations = join("\n", @{$constants});
713     my $methodsDeclarations = join("\n", keys %{$methods});
714
715     my $headerBody = << "EOF";
716 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
717 // Use of this source code is governed by a BSD-style license that can be
718 // found in the LICENSE file.
719 #ifndef ${className}_h
720 #define ${className}_h
721
722 ${forwardHeaders}
723
724 namespace $namespace {
725
726 $forwardDeclarations
727
728 class $className {
729 public:
730 $constructor
731
732 $constantDeclarations
733 $methodsDeclarations
734
735 private:
736 $footer
737 };
738
739 } // namespace $namespace
740 #endif // !defined(${className}_h)
741
742 EOF
743     return $headerBody;
744 }
745
746 sub generateSource
747 {
748     my $className = shift;
749     my $types = shift;
750     my $constants = shift;
751     my $methods = shift;
752
753     my @sourceContent = split("\r", $licenseTemplate);
754     push(@sourceContent, "\n#include \"config.h\"");
755     push(@sourceContent, "#include \"$className.h\"");
756     push(@sourceContent, "#include <wtf/text/StringConcatenate.h>");
757     push(@sourceContent, "#include <wtf/text/CString.h>");
758     push(@sourceContent, "");
759     push(@sourceContent, "#if ENABLE(INSPECTOR)");
760     push(@sourceContent, "");
761
762     my %headers;
763     foreach my $type (keys %{$types}) {
764         $headers{"#include \"" . $typeTransform{$type}->{"header"} . "\""} = 1 if !$typeTransform{$type}->{"header"} eq  "";
765     }
766     push(@sourceContent, sort keys %headers);
767     push(@sourceContent, "");
768     push(@sourceContent, "namespace $namespace {");
769     push(@sourceContent, "");
770     push (@sourceContent, join("\n", @{$constants}));
771     push(@sourceContent, "");
772     push(@sourceContent, @{$methods});
773     push(@sourceContent, "");
774     push(@sourceContent, "} // namespace $namespace");
775     push(@sourceContent, "");
776     push(@sourceContent, "#endif // ENABLE(INSPECTOR)");
777     push(@sourceContent, "");
778     return @sourceContent;
779 }
780
781 sub finish
782 {
783     my $object = shift;
784
785     open(my $SOURCE, ">$outputDir/$frontendClassName.cpp") || die "Couldn't open file $outputDir/$frontendClassName.cpp";
786     print $SOURCE join("\n", generateSource($frontendClassName, \%frontendTypes, \@frontendConstantDefinitions, \@frontendMethodsImpl));
787     close($SOURCE);
788     undef($SOURCE);
789
790     open(my $HEADER, ">$outputHeadersDir/$frontendClassName.h") || die "Couldn't open file $outputHeadersDir/$frontendClassName.h";
791     print $HEADER generateHeader($frontendClassName, \%frontendTypes, $frontendConstructor, \@frontendConstantDeclarations, \%frontendMethods, $frontendFooter);
792     close($HEADER);
793     undef($HEADER);
794
795     open($SOURCE, ">$outputDir/$backendClassName.cpp") || die "Couldn't open file $outputDir/$backendClassName.cpp";
796     print $SOURCE join("\n", generateSource($backendClassName, \%backendTypes, \@backendConstantDefinitions, \@backendMethodsImpl));
797     close($SOURCE);
798     undef($SOURCE);
799
800     open($HEADER, ">$outputHeadersDir/$backendClassName.h") || die "Couldn't open file $outputHeadersDir/$backendClassName.h";
801     print $HEADER join("\n", generateHeader($backendClassName, \%backendTypes, $backendConstructor, \@backendConstantDeclarations, \%backendMethods, $backendFooter));
802     close($HEADER);
803     undef($HEADER);
804
805     open(my $JS_STUB, ">$outputDir/$backendJSStubName.js") || die "Couldn't open file $outputDir/$backendJSStubName.js";
806     print $JS_STUB join("\n", @backendStubJS);
807     close($JS_STUB);
808     undef($JS_STUB);
809 }
810
811 1;