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