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