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