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