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