| Index: chrome/test/chromedriver/chrome_impl.cc
|
| diff --git a/chrome/test/chromedriver/chrome_impl.cc b/chrome/test/chromedriver/chrome_impl.cc
|
| index b7212b780eb983669754acb1d8155863985d7a56..adbfab9acd04325df27646134fd2bff2f0540208 100644
|
| --- a/chrome/test/chromedriver/chrome_impl.cc
|
| +++ b/chrome/test/chromedriver/chrome_impl.cc
|
| @@ -13,6 +13,7 @@
|
| #include "base/time.h"
|
| #include "base/values.h"
|
| #include "chrome/test/chromedriver/devtools_client_impl.h"
|
| +#include "chrome/test/chromedriver/dom_tracker.h"
|
| #include "chrome/test/chromedriver/js.h"
|
| #include "chrome/test/chromedriver/net/net_util.h"
|
| #include "chrome/test/chromedriver/net/sync_websocket_impl.h"
|
| @@ -33,6 +34,19 @@ Status FetchPagesInfo(URLRequestContextGetter* context_getter,
|
| return internal::ParsePagesInfo(data, debugger_urls);
|
| }
|
|
|
| +Status GetContextIdForFrame(DomTracker* tracker,
|
| + const std::string& frame,
|
| + int* context_id) {
|
| + if (frame.empty()) {
|
| + *context_id = 0;
|
| + return Status(kOk);
|
| + }
|
| + Status status = tracker->GetContextIdForFrame(frame, context_id);
|
| + if (status.IsError())
|
| + return status;
|
| + return Status(kOk);
|
| +}
|
| +
|
| } // namespace
|
|
|
| ChromeImpl::ChromeImpl(base::ProcessHandle process,
|
| @@ -43,7 +57,8 @@ ChromeImpl::ChromeImpl(base::ProcessHandle process,
|
| : process_(process),
|
| context_getter_(context_getter),
|
| port_(port),
|
| - socket_factory_(socket_factory) {
|
| + socket_factory_(socket_factory),
|
| + dom_tracker_(new DomTracker()) {
|
| if (user_data_dir->IsValid()) {
|
| CHECK(user_data_dir_.Set(user_data_dir->Take()));
|
| }
|
| @@ -66,8 +81,17 @@ Status ChromeImpl::Init() {
|
| if (debugger_urls.empty())
|
| return Status(kUnknownError, "unable to discover open pages");
|
| client_.reset(new DevToolsClientImpl(
|
| - socket_factory_, debugger_urls.front(), NULL));
|
| - return Status(kOk);
|
| + socket_factory_, debugger_urls.front(), dom_tracker_.get()));
|
| +
|
| + // Perform necessary configuration of the DevTools client.
|
| + // Fetch the root document node so that Inspector will push DOM node
|
| + // information to the client.
|
| + base::DictionaryValue params;
|
| + Status status = client_->SendCommand("DOM.getDocument", params);
|
| + if (status.IsError())
|
| + return status;
|
| + // Enable runtime events to allow tracking execution context creation.
|
| + return client_->SendCommand("Runtime.enable", params);
|
| }
|
|
|
| Status ChromeImpl::Load(const std::string& url) {
|
| @@ -76,12 +100,19 @@ Status ChromeImpl::Load(const std::string& url) {
|
| return client_->SendCommand("Page.navigate", params);
|
| }
|
|
|
| -Status ChromeImpl::EvaluateScript(const std::string& expression,
|
| +Status ChromeImpl::EvaluateScript(const std::string& frame,
|
| + const std::string& expression,
|
| scoped_ptr<base::Value>* result) {
|
| - return internal::EvaluateScript(client_.get(), expression, result);
|
| + int context_id;
|
| + Status status = GetContextIdForFrame(dom_tracker_.get(), frame, &context_id);
|
| + if (status.IsError())
|
| + return status;
|
| + return internal::EvaluateScriptAndGetValue(
|
| + client_.get(), context_id, expression, result);
|
| }
|
|
|
| -Status ChromeImpl::CallFunction(const std::string& function,
|
| +Status ChromeImpl::CallFunction(const std::string& frame,
|
| + const std::string& function,
|
| const base::ListValue& args,
|
| scoped_ptr<base::Value>* result) {
|
| std::string json;
|
| @@ -91,7 +122,23 @@ Status ChromeImpl::CallFunction(const std::string& function,
|
| kCallFunctionScript,
|
| function.c_str(),
|
| json.c_str());
|
| - return EvaluateScript(expression, result);
|
| + return EvaluateScript(frame, expression, result);
|
| +}
|
| +
|
| +Status ChromeImpl::GetFrameByFunction(const std::string& frame,
|
| + const std::string& function,
|
| + const base::ListValue& args,
|
| + std::string* out_frame) {
|
| + int context_id;
|
| + Status status = GetContextIdForFrame(dom_tracker_.get(), frame, &context_id);
|
| + if (status.IsError())
|
| + return status;
|
| + int node_id;
|
| + status = internal::GetNodeIdFromFunction(
|
| + client_.get(), context_id, function, args, &node_id);
|
| + if (status.IsError())
|
| + return status;
|
| + return dom_tracker_->GetFrameIdForNode(node_id, out_frame);
|
| }
|
|
|
| Status ChromeImpl::Quit() {
|
| @@ -126,11 +173,15 @@ Status ParsePagesInfo(const std::string& data,
|
| }
|
|
|
| Status EvaluateScript(DevToolsClient* client,
|
| + int context_id,
|
| const std::string& expression,
|
| - scoped_ptr<base::Value>* result) {
|
| + EvaluateScriptReturnType return_type,
|
| + scoped_ptr<base::DictionaryValue>* result) {
|
| base::DictionaryValue params;
|
| params.SetString("expression", expression);
|
| - params.SetBoolean("returnByValue", true);
|
| + if (context_id)
|
| + params.SetInteger("contextId", context_id);
|
| + params.SetBoolean("returnByValue", return_type == ReturnByValue);
|
| scoped_ptr<base::DictionaryValue> cmd_result;
|
| Status status = client->SendCommandAndGetResult(
|
| "Runtime.evaluate", params, &cmd_result);
|
| @@ -147,28 +198,104 @@ Status EvaluateScript(DevToolsClient* client,
|
| "Runtime.evaluate threw exception: " + description);
|
| }
|
|
|
| + base::DictionaryValue* unscoped_result;
|
| + if (!cmd_result->GetDictionary("result", &unscoped_result))
|
| + return Status(kUnknownError, "evaluate missing dictionary 'result'");
|
| + result->reset(unscoped_result->DeepCopy());
|
| + return Status(kOk);
|
| +}
|
| +
|
| +Status EvaluateScriptAndGetObject(DevToolsClient* client,
|
| + int context_id,
|
| + const std::string& expression,
|
| + std::string* object_id) {
|
| + scoped_ptr<base::DictionaryValue> result;
|
| + Status status = EvaluateScript(client, context_id, expression, ReturnByObject,
|
| + &result);
|
| + if (status.IsError())
|
| + return status;
|
| + if (!result->GetString("objectId", object_id))
|
| + return Status(kUnknownError, "evaluate missing string 'objectId'");
|
| + return Status(kOk);
|
| +}
|
| +
|
| +Status EvaluateScriptAndGetValue(DevToolsClient* client,
|
| + int context_id,
|
| + const std::string& expression,
|
| + scoped_ptr<base::Value>* result) {
|
| + scoped_ptr<base::DictionaryValue> temp_result;
|
| + Status status = EvaluateScript(client, context_id, expression, ReturnByValue,
|
| + &temp_result);
|
| + if (status.IsError())
|
| + return status;
|
| +
|
| std::string type;
|
| - if (!cmd_result->GetString("result.type", &type))
|
| - return Status(kUnknownError, "Runtime.evaluate missing result.type");
|
| + if (!temp_result->GetString("type", &type))
|
| + return Status(kUnknownError, "Runtime.evaluate missing string 'type'");
|
|
|
| if (type == "undefined") {
|
| result->reset(base::Value::CreateNullValue());
|
| } else {
|
| int status_code;
|
| - if (!cmd_result->GetInteger("result.value.status", &status_code)) {
|
| + if (!temp_result->GetInteger("value.status", &status_code)) {
|
| return Status(kUnknownError,
|
| - "Runtime.evaluate missing result.value.status");
|
| + "Runtime.evaluate missing int 'value.status'");
|
| }
|
| if (status_code != kOk)
|
| return Status(static_cast<StatusCode>(status_code));
|
| base::Value* unscoped_value;
|
| - if (!cmd_result->Get("result.value.value", &unscoped_value)) {
|
| + if (!temp_result->Get("value.value", &unscoped_value)) {
|
| return Status(kUnknownError,
|
| - "Runtime.evaluate missing result.value.value");
|
| + "Runtime.evaluate missing 'value.value'");
|
| }
|
| result->reset(unscoped_value->DeepCopy());
|
| }
|
| return Status(kOk);
|
| }
|
|
|
| +Status GetNodeIdFromFunction(DevToolsClient* client,
|
| + int context_id,
|
| + const std::string& function,
|
| + const base::ListValue& args,
|
| + int* node_id) {
|
| + std::string json;
|
| + base::JSONWriter::Write(&args, &json);
|
| + std::string expression = base::StringPrintf(
|
| + "(%s).apply(null, [%s, %s, true])",
|
| + kCallFunctionScript,
|
| + function.c_str(),
|
| + json.c_str());
|
| +
|
| + std::string element_id;
|
| + Status status = internal::EvaluateScriptAndGetObject(
|
| + client, context_id, expression, &element_id);
|
| + if (status.IsError())
|
| + return status;
|
| +
|
| + scoped_ptr<base::DictionaryValue> cmd_result;
|
| + {
|
| + base::DictionaryValue params;
|
| + params.SetString("objectId", element_id);
|
| + status = client->SendCommandAndGetResult(
|
| + "DOM.requestNode", params, &cmd_result);
|
| + }
|
| + {
|
| + // Release the remote object before doing anything else.
|
| + base::DictionaryValue params;
|
| + params.SetString("objectId", element_id);
|
| + Status release_status =
|
| + client->SendCommand("Runtime.releaseObject", params);
|
| + if (release_status.IsError()) {
|
| + LOG(ERROR) << "Failed to release remote object: "
|
| + << release_status.message();
|
| + }
|
| + }
|
| + if (status.IsError())
|
| + return status;
|
| +
|
| + if (!cmd_result->GetInteger("nodeId", node_id))
|
| + return Status(kUnknownError, "DOM.requestNode missing int 'nodeId'");
|
| + return Status(kOk);
|
| +}
|
| +
|
| } // namespace internal
|
|
|