2010-08-23 Ilya Tikhonovsky <loislo@chromium.org>
authorloislo@chromium.org <loislo@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 23 Aug 2010 14:07:09 +0000 (14:07 +0000)
committerloislo@chromium.org <loislo@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 23 Aug 2010 14:07:09 +0000 (14:07 +0000)
        Reviewed by Yury Semikhatsky.

        WebInspector: inspector protocol should be switched from array based
        message format to object based message format.
        Almost all the protocol related things is generated by CodeGeneratorInspector.pm
        It was changed a bit. As result InspectorBackendStub.js wraps the
        calls into Request objects. InspectorBackendDispatcher.cpp unwraps
        these objects and calls corresponding agents. These two files and
        RemoteInspectorFrontend are generated by CodeGeneratorInspector.pm
        Dispatching part of WebInspector also was adjusted for handling Event
        objects produced by RemoteInspectorFrontend.cpp and Response objects
        produced by InspectorBackendDispatcher.cpp
        https://bugs.webkit.org/show_bug.cgi?id=44338

        * inspector/CodeGeneratorInspector.pm:
        * inspector/Inspector.idl:
        * inspector/InspectorController.cpp:
        (WebCore::InspectorController::setAttachedWindow):
        * inspector/InspectorController.h:
        * inspector/InspectorFrontendClientLocal.cpp:
        (WebCore::InspectorFrontendClientLocal::setAttachedWindow):
        * inspector/InspectorValues.h:
        (WebCore::InspectorObject::find):
        * inspector/front-end/Callback.js:
        (WebInspector.Callback.prototype.processResponse):
        * inspector/front-end/inspector.js:
        (WebInspector.dispatch):
        (WebInspector_syncDispatch):
        (WebInspector.dispatchMessageFromBackend):
        (WebInspector.reportProtocolError):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@65809 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebCore/ChangeLog
WebCore/inspector/CodeGeneratorInspector.pm
WebCore/inspector/Inspector.idl
WebCore/inspector/InspectorController.cpp
WebCore/inspector/InspectorController.h
WebCore/inspector/InspectorFrontendClientLocal.cpp
WebCore/inspector/InspectorValues.h
WebCore/inspector/front-end/Callback.js
WebCore/inspector/front-end/inspector.js

index b526408..3069b9b 100644 (file)
@@ -1,3 +1,36 @@
+2010-08-23  Ilya Tikhonovsky  <loislo@chromium.org>
+
+        Reviewed by Yury Semikhatsky.
+
+        WebInspector: inspector protocol should be switched from array based
+        message format to object based message format.
+        Almost all the protocol related things is generated by CodeGeneratorInspector.pm
+        It was changed a bit. As result InspectorBackendStub.js wraps the
+        calls into Request objects. InspectorBackendDispatcher.cpp unwraps
+        these objects and calls corresponding agents. These two files and
+        RemoteInspectorFrontend are generated by CodeGeneratorInspector.pm
+        Dispatching part of WebInspector also was adjusted for handling Event
+        objects produced by RemoteInspectorFrontend.cpp and Response objects
+        produced by InspectorBackendDispatcher.cpp
+        https://bugs.webkit.org/show_bug.cgi?id=44338
+
+        * inspector/CodeGeneratorInspector.pm:
+        * inspector/Inspector.idl:
+        * inspector/InspectorController.cpp:
+        (WebCore::InspectorController::setAttachedWindow):
+        * inspector/InspectorController.h:
+        * inspector/InspectorFrontendClientLocal.cpp:
+        (WebCore::InspectorFrontendClientLocal::setAttachedWindow):
+        * inspector/InspectorValues.h:
+        (WebCore::InspectorObject::find):
+        * inspector/front-end/Callback.js:
+        (WebInspector.Callback.prototype.processResponse):
+        * inspector/front-end/inspector.js:
+        (WebInspector.dispatch):
+        (WebInspector_syncDispatch):
+        (WebInspector.dispatchMessageFromBackend):
+        (WebInspector.reportProtocolError):
+
 2010-08-19  Jeremy Orlow  <jorlow@chromium.org>
 
         Reviewed by Steve Block.
index e647c90..5ecbfca 100644 (file)
@@ -17,31 +17,31 @@ $typeTransform{"InspectorClient"} = {
 $typeTransform{"Backend"} = {
     "forward" => "InspectorBackend",
     "header" => "InspectorBackend.h",
-    "handlerAccessor" => "m_inspectorController->inspectorBackend()",
+    "domainAccessor" => "m_inspectorController->inspectorBackend()",
 };
 $typeTransform{"Controller"} = {
     "forwardHeader" => "InspectorController.h",
-    "handlerAccessor" => "m_inspectorController",
+    "domainAccessor" => "m_inspectorController",
 };
 $typeTransform{"Debug"} = {
     "forward" => "InspectorDebuggerAgent",
     "header" => "InspectorDebuggerAgent.h",
-    "handlerAccessor" => "m_inspectorController->debuggerAgent()",
+    "domainAccessor" => "m_inspectorController->debuggerAgent()",
 };
 $typeTransform{"DOM"} = {
     "forward" => "InspectorDOMAgent",
     "header" => "InspectorDOMAgent.h",
-    "handlerAccessor" => "m_inspectorController->domAgent()",
+    "domainAccessor" => "m_inspectorController->domAgent()",
 };
 $typeTransform{"ApplicationCache"} = {
     "forward" => "InspectorApplicationCacheAgent",
     "header" => "InspectorApplicationCacheAgent.h",
-    "handlerAccessor" => "m_inspectorController->applicationCacheAgent()",
+    "domainAccessor" => "m_inspectorController->applicationCacheAgent()",
 };
 $typeTransform{"Profiler"} = {
     "forward" => "InspectorProfilerAgent",
     "header" => "InspectorProfilerAgent.h",
-    "handlerAccessor" => "m_inspectorController->profilerAgent()",
+    "domainAccessor" => "m_inspectorController->profilerAgent()",
 };
 $typeTransform{"Frontend"} = {
     "forward" => "RemoteInspectorFrontend",
@@ -56,7 +56,7 @@ $typeTransform{"Object"} = {
     "defaultValue" => "InspectorObject::create()",
     "forward" => "InspectorObject",
     "header" => "InspectorValues.h",
-    "accessorSuffix" => "Object"
+    "JSONType" => "Object"
 };
 $typeTransform{"Array"} = {
     "param" => "PassRefPtr<InspectorArray>",
@@ -64,7 +64,7 @@ $typeTransform{"Array"} = {
     "defaultValue" => "InspectorArray::create()",
     "forward" => "InspectorArray",
     "header" => "InspectorValues.h",
-    "accessorSuffix" => "Array"
+    "JSONType" => "Array"
 };
 $typeTransform{"Value"} = {
     "param" => "PassRefPtr<InspectorValue>",
@@ -72,14 +72,15 @@ $typeTransform{"Value"} = {
     "defaultValue" => "InspectorValue::null()",
     "forward" => "InspectorValue",
     "header" => "InspectorValues.h",
-    "accessorSuffix" => "Value"
+    "JSONType" => "Value"
 };
 $typeTransform{"String"} = {
     "param" => "const String&",
     "variable" => "String",
+    "defaultValue" => "\"\"",
     "forwardHeader" => "wtf/Forward.h",
     "header" => "PlatformString.h",
-    "accessorSuffix" => "String"
+    "JSONType" => "String"
 };
 $typeTransform{"long"} = {
     "param" => "long",
@@ -87,7 +88,7 @@ $typeTransform{"long"} = {
     "defaultValue" => "0",
     "forward" => "",
     "header" => "",
-    "accessorSuffix" => "Number"
+    "JSONType" => "Number"
 };
 $typeTransform{"int"} = {
     "param" => "int",
@@ -95,7 +96,7 @@ $typeTransform{"int"} = {
     "defaultValue" => "0",
     "forward" => "",
     "header" => "",
-    "accessorSuffix" => "Number",
+    "JSONType" => "Number",
 };
 $typeTransform{"unsigned long"} = {
     "param" => "unsigned long",
@@ -103,7 +104,7 @@ $typeTransform{"unsigned long"} = {
     "defaultValue" => "0u",
     "forward" => "",
     "header" => "",
-    "accessorSuffix" => "Number"
+    "JSONType" => "Number"
 };
 $typeTransform{"unsigned int"} = {
     "param" => "unsigned int",
@@ -111,7 +112,7 @@ $typeTransform{"unsigned int"} = {
     "defaultValue" => "0u",
     "forward" => "",
     "header" => "",
-    "accessorSuffix" => "Number"
+    "JSONType" => "Number"
 };
 $typeTransform{"boolean"} = {
     "param" => "bool",
@@ -119,7 +120,7 @@ $typeTransform{"boolean"} = {
     "defaultValue" => "false",
     "forward" => "",
     "header" => "",
-    "accessorSuffix" => "Bool"
+    "JSONType" => "Bool"
 };
 $typeTransform{"void"} = {
     "forward" => "",
@@ -162,10 +163,6 @@ my @frontendConstantDeclarations;
 my @frontendConstantDefinitions;
 my $frontendFooter;
 
-my $callId = new domSignature(); # it is just structure for describing parameters from IDLStructure.pm.
-$callId->type("long");
-$callId->name("callId");
-
 # Default constructor
 sub new
 {
@@ -222,9 +219,8 @@ sub GenerateInterface
     $backendTypes{"Controller"} = 1;
     $backendTypes{"InspectorClient"} = 1;
     $backendTypes{"PassRefPtr"} = 1;
-    $backendTypes{"Array"} = 1;
+    $backendTypes{"Object"} = 1;
 
-    push(@backendMethodsImpl, generateBackendPrivateFunctions());
     push(@backendMethodsImpl, generateBackendMessageParser());
     generateFunctions($interface);
 
@@ -259,7 +255,6 @@ sub generateFrontendFunction
     my @argsFiltered = grep($_->direction eq "out", @{$function->parameters}); # just keep only out parameters for frontend interface.
     map($frontendTypes{$_->type} = 1, @argsFiltered); # register required types.
     my $arguments = join(", ", map($typeTransform{$_->type}->{"param"} . " " . $_->name, @argsFiltered)); # prepare arguments for function signature.
-    my @pushArguments = map("    arguments->push" . $typeTransform{$_->type}->{"accessorSuffix"} . "(" . $_->name . ");", @argsFiltered);
 
     my $signature = "    void ${functionName}(${arguments});";
     if (!$frontendMethods{${signature}}) {
@@ -268,10 +263,14 @@ sub generateFrontendFunction
         my @function;
         push(@function, "void ${frontendClassName}::${functionName}(${arguments})");
         push(@function, "{");
-        push(@function, "    RefPtr<InspectorArray> arguments = InspectorArray::create();");
-        push(@function, "    arguments->pushString(\"$functionName\");");
+        push(@function, "    RefPtr<InspectorObject> ${functionName}Message = InspectorObject::create();");
+        push(@function, "    ${functionName}Message->setString(\"type\", \"event\");");
+        push(@function, "    ${functionName}Message->setString(\"event\", \"$functionName\");");
+        push(@function, "    RefPtr<InspectorObject> payloadDataObject = InspectorObject::create();");
+        my @pushArguments = map("    payloadDataObject->set" . $typeTransform{$_->type}->{"JSONType"} . "(\"" . $_->name . "\", " . $_->name . ");", @argsFiltered);
         push(@function, @pushArguments);
-        push(@function, "    m_inspectorClient->sendMessageToFrontend(arguments->toJSONString());");
+        push(@function, "    ${functionName}Message->setObject(\"data\", payloadDataObject);");
+        push(@function, "    m_inspectorClient->sendMessageToFrontend(${functionName}Message->toJSONString());");
 
         push(@function, "}");
         push(@function, "");
@@ -279,22 +278,6 @@ sub generateFrontendFunction
     }
 }
 
-sub generateBackendPrivateFunctions
-{
-    my $privateFunctions = << "EOF";
-static String formatWrongArgumentsCountMessage(unsigned expected, unsigned actual)
-{
-    return String::format("Wrong number of parameters: %d (expected: %d)", actual, expected);
-}
-
-static String formatWrongArgumentTypeMessage(unsigned position, const char* name, const char* expectedType)
-{
-    return String::format("Failed to convert parameter %d (%s) to %s", position, name, expectedType);
-}
-EOF
-    return split("\n", $privateFunctions);
-}
-
 sub generateBackendFunction
 {
     my $function = shift;
@@ -305,74 +288,90 @@ sub generateBackendFunction
     push(@backendConstantDefinitions, "const char* ${backendClassName}::${functionName}Cmd = \"${functionName}\";");
 
     map($backendTypes{$_->type} = 1, @{$function->parameters}); # register required types
-    my @inArgs = grep($_->direction eq "in", @{$function->parameters});
+    my @inArgs = grep($_->direction eq "in" && !($_->name eq "callId") , @{$function->parameters});
     my @outArgs = grep($_->direction eq "out", @{$function->parameters});
 
-    my $signature = "    void ${functionName}(PassRefPtr<InspectorArray> args);";
+    my $signature = "    void ${functionName}(long callId, InspectorObject* requestMessageObject);";
     !$backendMethods{${signature}} || die "Duplicate function was detected for signature '$signature'.";
     $backendMethods{${signature}} = $functionName;
 
     my @function;
-    push(@function, "void ${backendClassName}::${functionName}(PassRefPtr<InspectorArray> args)");
+    my $requestMessageObject = scalar(@inArgs) ? " requestMessageObject" : "";
+    push(@function, "void ${backendClassName}::${functionName}(long callId, InspectorObject*$requestMessageObject)");
     push(@function, "{");
-    push(@function, "    long callId = 0;");
+    push(@function, "    RefPtr<InspectorArray> protocolErrors = InspectorArray::create();");
     push(@function, "");
 
-    my $expectedParametersCount = scalar(@inArgs);
-    my $expectedParametersCountWithMethodName = scalar(@inArgs) + 1;
-    push(@function, "    if (args->length() != $expectedParametersCountWithMethodName) {");
-    push(@function, "        ASSERT_NOT_REACHED();");
-    push(@function, "        reportProtocolError(callId, ${functionName}Cmd, formatWrongArgumentsCountMessage(args->length() - 1, $expectedParametersCount));");
-    push(@function, "        return;");
-    push(@function, "    }");
+    my $domain = $function->signature->extendedAttributes->{"handler"} || "Controller";
+    my $domainAccessor = $typeTransform{$domain}->{"domainAccessor"};
+    $backendTypes{$domain} = 1;
+    push(@function, "    if (!$domainAccessor)");
+    push(@function, "        protocolErrors->pushString(String::format(\"Error: %s handler is not available.\", \"$domain\"));");
     push(@function, "");
 
-    my $i = 1; # zero element is the method name.
-    foreach my $parameter (@inArgs) {
-        my $type = $parameter->type;
-        my $argumentType = $typeTransform{$type}->{"variable"};
-        push(@function, "    $argumentType " . $parameter->name . ";") if !($parameter->name eq "callId");
-        push(@function, "    if (!args->get($i)->as" . $typeTransform{$type}->{"accessorSuffix"} . "(&" . $parameter->name . ")) {");
+    if (scalar(@inArgs)) {
+        # declare variables for all 'in' args;
+        push(@function, map("    " . $typeTransform{$_->type}->{"variable"} . " " . $_->name . " = " . $typeTransform{$_->type}->{"defaultValue"} . ";", @inArgs));
+
+        push(@function, "");
+        push(@function, "    RefPtr<InspectorObject> argumentsContainer;");
+        push(@function, "    if (!(argumentsContainer = requestMessageObject->getObject(\"arguments\"))) {");
         push(@function, "        ASSERT_NOT_REACHED();");
-        push(@function, "        reportProtocolError(callId, ${functionName}Cmd, formatWrongArgumentTypeMessage($i, \"" . $parameter->name . "\", \"$type\"));");
-        push(@function, "        return;");
+        push(@function, "        protocolErrors->pushString(String::format(\"Error: arguments object was not found.\"));");
+        push(@function, "    } else {");
+        push(@function, "        InspectorObject::const_iterator argumentsEndIterator = argumentsContainer->end();");
+
+        foreach my $parameter (@inArgs) {
+            my $name = $parameter->name;
+            my $type = $parameter->type;
+            my $variableType = $typeTransform{$type}->{"variable"};
+            my $JSONType = $typeTransform{$type}->{"JSONType"};
+
+            push(@function, "");
+            push(@function, "        InspectorObject::const_iterator ${name}ValueIterator = argumentsContainer->find(\"$name\");");
+            push(@function, "        if (${name}ValueIterator == argumentsEndIterator) {");
+            push(@function, "            ASSERT_NOT_REACHED();");
+            push(@function, "            protocolErrors->pushString(String::format(\"Error: Argument '%s' with type '%s' was not found.\", \"$name\", \"$JSONType\"));");
+            push(@function, "        } else {");
+            push(@function, "            if (!${name}ValueIterator->second->as$JSONType(&$name)) {");
+            push(@function, "                ASSERT_NOT_REACHED();");
+            push(@function, "                protocolErrors->pushString(String::format(\"Error: Argument '%s' has wrong type. It should be '%s'.\", \"$name\", \"$JSONType\"));");
+            push(@function, "            }");
+            push(@function, "        }");
+        }
         push(@function, "    }");
-        push(@function, "");
-        ++$i;
     }
 
-    my $handler = $function->signature->extendedAttributes->{"handler"} || "Controller";
-    my $handlerAccessor = $typeTransform{$handler}->{"handlerAccessor"};
-    $backendTypes{$handler} = 1;
-    push(@function, "    if (!$handlerAccessor) {");
-    push(@function, "        reportProtocolError(callId, ${functionName}Cmd, \"Error: $handler handler is not available.\");");
-    push(@function, "        return;");
-    push(@function, "    }");
-    push(@function, "");
+    # declare local variables for out arguments.
+    push(@function, map("    " . $typeTransform{$_->type}->{"variable"} . " " . $_->name . " = " . $typeTransform{$_->type}->{"defaultValue"} . ";", @outArgs));
 
+    my $args = join(", ", (map($_->name, @inArgs), map("&" . $_->name, @outArgs)));
+    push(@function, "    if (!protocolErrors->length())");
+    push(@function, "        $domainAccessor->$functionName($args);");
+    push(@function, "");
 
-    foreach (@outArgs) { # declare local variables for out arguments.
-        my $initializer = $typeTransform{$_->type}->{"defaultValue"} ? " = " . $typeTransform{$_->type}->{"defaultValue"} : "";
-        push(@function, "    " . $typeTransform{$_->type}->{"variable"} . " " . $_->name . "$initializer;");
+    push(@function, "    // use InspectorFrontend as a marker of WebInspector availability");
+    push(@function, "    if (callId && m_inspectorController->hasFrontend()) {");
+    push(@function, "        RefPtr<InspectorObject> responseMessage = InspectorObject::create();");
+    push(@function, "        responseMessage->setNumber(\"seq\", callId);");
+    push(@function, "        responseMessage->setString(\"type\", \"response\");");
+    push(@function, "        responseMessage->setString(\"domain\", \"$domain\");");
+    push(@function, "        responseMessage->setString(\"command\", \"$functionName\");");
+    push(@function, "        responseMessage->setBool(\"success\", !protocolErrors->length());");
+    push(@function, "");
+    push(@function, "        if (protocolErrors->length())");
+    push(@function, "            responseMessage->setArray(\"errors\", protocolErrors);");
+    if (scalar(@outArgs)) {
+        push(@function, "        else {");
+        push(@function, "            RefPtr<InspectorObject> responseData = InspectorObject::create();");
+        push(@function, map("            responseData->set" . $typeTransform{$_->type}->{"JSONType"} . "(\"" . $_->name . "\", " . $_->name . ");", @outArgs));
+        push(@function, "            responseMessage->setObject(\"data\", responseData);");
+        push(@function, "        }");
     }
+    push(@function, "        m_inspectorController->inspectorClient()->sendMessageToFrontend(responseMessage->toJSONString());");
+    push(@function, "    }");
 
-    my $args = join(", ", (grep(!($_ eq "callId"), map($_->name, @inArgs)), map("&" . $_->name, @outArgs)));
-    push(@function, "    $handlerAccessor->$functionName($args);");
-
-    # The results of function call should be transfered back to frontend.
-    if (scalar(grep($_->name eq "callId", @inArgs))) {
-        my @pushArguments = map("        arguments->push" . $typeTransform{$_->type}->{"accessorSuffix"} . "(" . $_->name . ");", @outArgs);
 
-        push(@function, "");
-        push(@function, "    // use InspectorFrontend as a marker of WebInspector availability");
-        push(@function, "    if (m_inspectorController->hasFrontend()) {");
-        push(@function, "        RefPtr<InspectorArray> arguments = InspectorArray::create();");
-        push(@function, "        arguments->pushString(\"processResponse\");");
-        push(@function, "        arguments->pushNumber(callId);");
-        push(@function, @pushArguments);
-        push(@function, "        m_inspectorController->inspectorClient()->sendMessageToFrontend(arguments->toJSONString());");
-        push(@function, "    }");
-    }
     push(@function, "}");
     push(@function, "");
     push(@backendMethodsImpl, @function);
@@ -384,17 +383,22 @@ sub generateBackendReportProtocolError
 
 void ${backendClassName}::reportProtocolError(const long callId, const String& method, const String& errorText) const
 {
-    RefPtr<InspectorArray> arguments = InspectorArray::create();
-    arguments->pushString("reportProtocolError");
-    arguments->pushNumber(callId);
-    arguments->pushString(method);
-    arguments->pushString(errorText);
-    m_inspectorController->inspectorClient()->sendMessageToFrontend(arguments->toJSONString());
+    RefPtr<InspectorObject> message = InspectorObject::create();
+    message->setNumber("seq", callId);
+    message->setString("type", "error");
+    message->setString("domain", "inspectorProtocol");
+    message->setString("command", method);
+    message->setBool("success", false);
+    RefPtr<InspectorArray> errors = InspectorArray::create();
+    errors->pushString(errorText);
+    message->setArray("errors", errors);
+    m_inspectorController->inspectorClient()->sendMessageToFrontend(message->toJSONString());
 }
 EOF
     return split("\n", $reportProtocolError);
 }
 
+
 sub generateBackendDispatcher
 {
     my @body;
@@ -405,9 +409,11 @@ sub generateBackendDispatcher
     my $backendDispatcherBody = << "EOF";
 void ${backendClassName}::dispatch(const String& message)
 {
-    typedef void (${backendClassName}::*CallHandler)(PassRefPtr<InspectorArray> args);
+    typedef void (${backendClassName}::*CallHandler)(long callId, InspectorObject* messageObject);
     typedef HashMap<String, CallHandler> DispatchMap;
     DEFINE_STATIC_LOCAL(DispatchMap, dispatchMap, );
+    long callId = 0;
+
     if (dispatchMap.isEmpty()) {
 $mapEntries
     }
@@ -415,38 +421,52 @@ $mapEntries
     RefPtr<InspectorValue> parsedMessage = InspectorValue::parseJSON(message);
     if (!parsedMessage) {
         ASSERT_NOT_REACHED();
-        reportProtocolError(0, "dispatch", "Error: Invalid message format. Message should be in JSON format.");
+        reportProtocolError(callId, "dispatch", "Error: Invalid message format. Message should be in JSON format.");
+        return;
+    }
+
+    RefPtr<InspectorObject> messageObject = parsedMessage->asObject();
+    if (!messageObject) {
+        ASSERT_NOT_REACHED();
+        reportProtocolError(callId, "dispatch", "Error: Invalid message format. The message should be a JSONified object.");
         return;
     }
 
-    RefPtr<InspectorArray> messageArray = parsedMessage->asArray();
-    if (!messageArray) {
+    RefPtr<InspectorValue> callIdValue = messageObject->get("seq");
+    if (!callIdValue) {
         ASSERT_NOT_REACHED();
-        reportProtocolError(0, "dispatch", "Error: Invalid message format. The message should be a JSONified array of arguments.");
+        reportProtocolError(callId, "dispatch", "Error: Invalid message format. 'seq' property was not found in the request.");
         return;
     }
 
-    if (!messageArray->length()) {
+    if (!callIdValue->asNumber(&callId)) {
         ASSERT_NOT_REACHED();
-        reportProtocolError(0, "dispatch", "Error: Invalid message format. Empty message was received.");
+        reportProtocolError(callId, "dispatch", "Error: Invalid message format. the type of 'seq' property should be number.");
         return;
     }
 
-    String methodName;
-    if (!messageArray->get(0)->asString(&methodName)) {
+    RefPtr<InspectorValue> commandValue = messageObject->get("command");
+    if (!commandValue) {
         ASSERT_NOT_REACHED();
-        reportProtocolError(0, "dispatch", "Error: Invalid message format. The first element of the message should be method name.");
+        reportProtocolError(callId, "dispatch", "Error: Invalid message format. 'command' property wasn't found.");
         return;
     }
 
-    HashMap<String, CallHandler>::iterator it = dispatchMap.find(methodName);
+    String command;
+    if (!commandValue->asString(&command)) {
+        ASSERT_NOT_REACHED();
+        reportProtocolError(callId, "dispatch", "Error: Invalid message format. The type of 'command' property should be string.");
+        return;
+    }
+
+    HashMap<String, CallHandler>::iterator it = dispatchMap.find(command);
     if (it == dispatchMap.end()) {
         ASSERT_NOT_REACHED();
-        reportProtocolError(0, "dispatch", String::format("Error: Invalid method name. '%s' wasn't found.", methodName.utf8().data()));
+        reportProtocolError(callId, "dispatch", String::format("Error: Invalid command was received. '%s' wasn't found.", command.utf8().data()));
         return;
     }
 
-    ((*this).*it->second)(messageArray);
+    ((*this).*it->second)(callId, messageObject.get());
 }
 EOF
     return split("\n", $backendDispatcherBody);
@@ -460,14 +480,18 @@ bool ${backendClassName}::getCommandName(const String& message, String* result)
     RefPtr<InspectorValue> value = InspectorValue::parseJSON(message);
     if (!value)
         return false;
-    RefPtr<InspectorArray> array = value->asArray();
-    if (!array)
+
+    RefPtr<InspectorObject> object = value->asObject();
+    if (!object)
         return false;
 
-    if (!array->length())
+    RefPtr<InspectorValue> commandValue = object->get("command");
+    if (!commandValue)
         return false;
-    return array->get(0)->asString(result);
+
+    return commandValue->asString(result);
 }
+
 EOF
     return split("\n", $messageParserBody);
 }
@@ -476,7 +500,20 @@ sub generateBackendStubJS
 {
     my $interface = shift;
     my @backendFunctions = grep(!$_->signature->extendedAttributes->{"notify"}, @{$interface->functions});
-    my @JSStubs = map("    this._registerDelegate(\"" . $_->signature->name . "\");", @backendFunctions);
+    my @JSStubs;
+
+    foreach my $function (@backendFunctions) {
+        my $name = $function->signature->name;
+        my $domain = $function->signature->extendedAttributes->{"handler"};
+        my $argumentNames = join(",", map("\"" . $_->name . "\": null", grep($_->direction eq "in", @{$function->parameters})));
+        push(@JSStubs, "    this._registerDelegate('{" .
+            "\"seq\": 0, " .
+            "\"type\": \"request\", " .
+            "\"domain\": \"$domain\", " .
+            "\"command\": \"$name\", " .
+            "\"arguments\": {$argumentNames}" .
+        "}');");
+    }
 
     my $JSStubs = join("\n", @JSStubs);
     my $inspectorBackendStubJS = << "EOF";
@@ -488,14 +525,26 @@ $JSStubs
 }
 
 WebInspector.InspectorBackendStub.prototype = {
-    _registerDelegate: function(methodName)
+    _registerDelegate: function(commandInfo)
     {
-        this[methodName] = this.sendMessageToBackend.bind(this, methodName);
+        var commandObject = JSON.parse(commandInfo);
+        this[commandObject.command] = this.sendMessageToBackend.bind(this, commandInfo);
     },
 
     sendMessageToBackend: function()
     {
-        var message = JSON.stringify(Array.prototype.slice.call(arguments));
+        var args = Array.prototype.slice.call(arguments);
+        var request = JSON.parse(args.shift());
+        for (var key in request.arguments) {
+            if (key === "callId")
+                request.seq = args.shift();
+            else
+                request.arguments[key] = args.shift();
+        }
+        if (args.length === 1 && typeof args[0] === "function")
+            request.seq = WebInspector.Callback.wrap(args[0]);
+
+        var message = JSON.stringify(request);
         InspectorFrontendHost.sendMessageToBackend(message);
     }
 }
index 392d1df..203636a 100644 (file)
@@ -57,6 +57,7 @@ module core {
         [notify] void resourceTrackingWasDisabled();
         [notify] void searchingForNodeWasEnabled();
         [notify] void searchingForNodeWasDisabled();
+        [notify] void setAttachedWindow(out boolean attached);
         [notify] void setChildNodes(out long parentId, out Array nodes);
         [notify] void setDetachedRoot(out Object root);
         [notify] void setDocument(out Value root);
index 89c9132..d5f9a3b 100644 (file)
@@ -420,6 +420,15 @@ void InspectorController::setInspectorFrontendClient(PassOwnPtr<InspectorFronten
     m_inspectorFrontendClient = client;
 }
 
+void InspectorController::setAttachedWindow(bool attached)
+{
+    if (!m_remoteFrontend) {
+        ASSERT_NOT_REACHED();
+        return;
+    }
+    m_remoteFrontend->setAttachedWindow(attached);
+}
+
 void InspectorController::inspectedWindowScriptObjectCleared(Frame* frame)
 {
     // If the page is supposed to serve as InspectorFrontend notify inspetor frontend
index 4fe67ce..bdb7b29 100644 (file)
@@ -147,6 +147,7 @@ public:
 
     void setInspectorFrontendClient(PassOwnPtr<InspectorFrontendClient> client);
     bool hasInspectorFrontendClient() const { return m_inspectorFrontendClient; }
+    void setAttachedWindow(bool attached);
 
     void inspectedWindowScriptObjectCleared(Frame*);
 
index b45dd34..0ee0985 100644 (file)
@@ -121,15 +121,7 @@ void InspectorFrontendClientLocal::moveWindowBy(float x, float y)
 
 void InspectorFrontendClientLocal::setAttachedWindow(bool attached)
 {
-    ScriptObject webInspectorObj;
-    if (!ScriptGlobalObject::get(m_frontendScriptState, "WebInspector", webInspectorObj)) {
-        ASSERT_NOT_REACHED();
-        return;
-    }
-    ScriptFunctionCall function(webInspectorObj, "dispatch"); 
-    function.appendArgument("setAttachedWindow");
-    function.appendArgument(attached);
-    function.call();
+    m_inspectorController->setAttachedWindow(attached);
 }
 
 void InspectorFrontendClientLocal::restoreAttachedWindowHeight()
index 4bc36f2..473ad21 100644 (file)
@@ -176,6 +176,7 @@ public:
     void setObject(const String& name, PassRefPtr<InspectorObject>);
     void setArray(const String& name, PassRefPtr<InspectorArray>);
 
+    const_iterator find(const String& name) const;
     bool getBool(const String& name, bool* output) const;
     bool getNumber(const String& name, double* output) const;
     bool getString(const String& name, String* output) const;
@@ -224,6 +225,11 @@ private:
     Vector<RefPtr<InspectorValue> > m_data;
 };
 
+inline InspectorObject::const_iterator InspectorObject::find(const String& name) const
+{
+    return m_data.find(name);
+}
+
 inline void InspectorObject::setBool(const String& name, bool value)
 {
     setValue(name, InspectorBasicValue::create(value));
index d8163fe..0621fd1 100644 (file)
@@ -42,9 +42,8 @@ WebInspector.Callback.prototype = {
         return callbackId;
     },
 
-    processResponse: function(callbackId, opt_vararg)
+    processResponse: function(callbackId, args)
     {
-        var args = Array.prototype.slice.call(arguments, 1);
         var callback = this._callbacks[callbackId];
         callback.apply(null, args);
         delete this._callbacks[callbackId];
index 52ae738..79b0e0e 100644 (file)
@@ -589,20 +589,12 @@ var windowLoaded = function()
 
 window.addEventListener("load", windowLoaded, false);
 
-WebInspector.dispatch = function() {
-    var methodName = arguments[0];
-    var parameters = Array.prototype.slice.call(arguments, 1);
-
+WebInspector.dispatch = function(message) {
     // We'd like to enforce asynchronous interaction between the inspector controller and the frontend.
     // This is important to LayoutTests.
     function delayDispatch()
     {
-        if (!(methodName in WebInspector)) {
-            console.error("Attempted to dispatch unimplemented WebInspector method: %s", methodName);
-            return;
-        }
-
-        WebInspector[methodName].apply(WebInspector, parameters);
+        WebInspector_syncDispatch(message);
         WebInspector.pendingDispatches--;
     }
     WebInspector.pendingDispatches++;
@@ -612,21 +604,41 @@ WebInspector.dispatch = function() {
 // This function is purposely put into the global scope for easy access.
 WebInspector_syncDispatch = function(message)
 {
-    var args = JSON.parse(message);
-    var methodName = args[0];
-    var parameters = args.slice(1);
-    WebInspector[methodName].apply(WebInspector, parameters);
+    var messageObject = (typeof message === "string") ? JSON.parse(message) : message;
+    if (messageObject.type === "response" && !messageObject.success) {
+        WebInspector.removeResponseCallbackEntry(messageObject.seq)
+        WebInspector.reportProtocolError(messageObject);
+        return;
+    }
+
+    var arguments = [];
+    if (messageObject.data)
+        for (var key in messageObject.data)
+            arguments.push(messageObject.data[key]);
+
+    if (messageObject.type === "event") {
+        if (!messageObject.event in WebInspector) {
+            console.error("Attempted to dispatch unimplemented WebInspector method: %s", messageObject.event);
+            return;
+        }
+        WebInspector[messageObject.event].apply(WebInspector, arguments);
+    }
+
+    if (messageObject.type === "response")
+        WebInspector.processResponse(messageObject.seq, arguments);
 }
 
-WebInspector.dispatchMessageFromBackend = function(arguments)
+WebInspector.dispatchMessageFromBackend = function(messageObject)
 {
-    WebInspector.dispatch.apply(this, arguments);
+    WebInspector.dispatch(messageObject);
 }
 
-WebInspector.reportProtocolError = function(callId, methodName, errorText)
+WebInspector.reportProtocolError = function(messageObject)
 {
-    console.error("InspectorBackend." + methodName + " failed with error text: '" + errorText + "'");
-    WebInspector.removeResponseCallbackEntry(callId);
+    console.error("Error: InspectorBackend." + messageObject.command + " failed.");
+    for (var error in messageObject.errors)
+        console.error("    " + error);
+    WebInspector.removeResponseCallbackEntry(messageObject.seq);
 }
 
 WebInspector.windowResize = function(event)