1 // Copyright (c) 2013 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 #include "chrome/test/chromedriver/chrome/web_view_impl.h"
6
7 #include "base/bind.h"
8 #include "base/files/file_path.h"
9 #include "base/json/json_writer.h"
10 #include "base/logging.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/threading/platform_thread.h"
14 #include "base/time/time.h"
15 #include "base/values.h"
16 #include "chrome/test/chromedriver/chrome/debugger_tracker.h"
17 #include "chrome/test/chromedriver/chrome/devtools_client_impl.h"
18 #include "chrome/test/chromedriver/chrome/dom_tracker.h"
19 #include "chrome/test/chromedriver/chrome/frame_tracker.h"
20 #include "chrome/test/chromedriver/chrome/geolocation_override_manager.h"
21 #include "chrome/test/chromedriver/chrome/heap_snapshot_taker.h"
22 #include "chrome/test/chromedriver/chrome/javascript_dialog_manager.h"
23 #include "chrome/test/chromedriver/chrome/js.h"
24 #include "chrome/test/chromedriver/chrome/mobile_emulation_override_manager.h"
25 #include "chrome/test/chromedriver/chrome/navigation_tracker.h"
26 #include "chrome/test/chromedriver/chrome/status.h"
27 #include "chrome/test/chromedriver/chrome/ui_events.h"
28 #include "chrome/test/chromedriver/chrome/version.h"
29
30 namespace {
31
GetContextIdForFrame(FrameTracker * tracker,const std::string & frame,int * context_id)32 Status GetContextIdForFrame(FrameTracker* tracker,
33 const std::string& frame,
34 int* context_id) {
35 if (frame.empty()) {
36 *context_id = 0;
37 return Status(kOk);
38 }
39 Status status = tracker->GetContextIdForFrame(frame, context_id);
40 if (status.IsError())
41 return status;
42 return Status(kOk);
43 }
44
GetAsString(MouseEventType type)45 const char* GetAsString(MouseEventType type) {
46 switch (type) {
47 case kPressedMouseEventType:
48 return "mousePressed";
49 case kReleasedMouseEventType:
50 return "mouseReleased";
51 case kMovedMouseEventType:
52 return "mouseMoved";
53 default:
54 return "";
55 }
56 }
57
GetAsString(TouchEventType type)58 const char* GetAsString(TouchEventType type) {
59 switch (type) {
60 case kTouchStart:
61 return "touchStart";
62 case kTouchEnd:
63 return "touchEnd";
64 case kTouchMove:
65 return "touchMove";
66 default:
67 return "";
68 }
69 }
70
GetPointStateString(TouchEventType type)71 const char* GetPointStateString(TouchEventType type) {
72 switch (type) {
73 case kTouchStart:
74 return "touchPressed";
75 case kTouchEnd:
76 return "touchReleased";
77 case kTouchMove:
78 return "touchMoved";
79 default:
80 return "";
81 }
82 }
83
GetAsString(MouseButton button)84 const char* GetAsString(MouseButton button) {
85 switch (button) {
86 case kLeftMouseButton:
87 return "left";
88 case kMiddleMouseButton:
89 return "middle";
90 case kRightMouseButton:
91 return "right";
92 case kNoneMouseButton:
93 return "none";
94 default:
95 return "";
96 }
97 }
98
GetAsString(KeyEventType type)99 const char* GetAsString(KeyEventType type) {
100 switch (type) {
101 case kKeyDownEventType:
102 return "keyDown";
103 case kKeyUpEventType:
104 return "keyUp";
105 case kRawKeyDownEventType:
106 return "rawKeyDown";
107 case kCharEventType:
108 return "char";
109 default:
110 return "";
111 }
112 }
113
114 } // namespace
115
WebViewImpl(const std::string & id,const BrowserInfo * browser_info,scoped_ptr<DevToolsClient> client,const DeviceMetrics * device_metrics)116 WebViewImpl::WebViewImpl(const std::string& id,
117 const BrowserInfo* browser_info,
118 scoped_ptr<DevToolsClient> client,
119 const DeviceMetrics* device_metrics)
120 : id_(id),
121 browser_info_(browser_info),
122 dom_tracker_(new DomTracker(client.get())),
123 frame_tracker_(new FrameTracker(client.get())),
124 navigation_tracker_(new NavigationTracker(client.get(), browser_info)),
125 dialog_manager_(new JavaScriptDialogManager(client.get())),
126 mobile_emulation_override_manager_(
127 new MobileEmulationOverrideManager(client.get(), device_metrics)),
128 geolocation_override_manager_(
129 new GeolocationOverrideManager(client.get())),
130 heap_snapshot_taker_(new HeapSnapshotTaker(client.get())),
131 debugger_(new DebuggerTracker(client.get())),
132 client_(client.release()) {}
133
~WebViewImpl()134 WebViewImpl::~WebViewImpl() {}
135
GetId()136 std::string WebViewImpl::GetId() {
137 return id_;
138 }
139
WasCrashed()140 bool WebViewImpl::WasCrashed() {
141 return client_->WasCrashed();
142 }
143
ConnectIfNecessary()144 Status WebViewImpl::ConnectIfNecessary() {
145 return client_->ConnectIfNecessary();
146 }
147
HandleReceivedEvents()148 Status WebViewImpl::HandleReceivedEvents() {
149 return client_->HandleReceivedEvents();
150 }
151
Load(const std::string & url)152 Status WebViewImpl::Load(const std::string& url) {
153 // Javascript URLs will cause a hang while waiting for the page to stop
154 // loading, so just disallow.
155 if (StartsWithASCII(url, "javascript:", false))
156 return Status(kUnknownError, "unsupported protocol");
157 base::DictionaryValue params;
158 params.SetString("url", url);
159 return client_->SendCommand("Page.navigate", params);
160 }
161
Reload()162 Status WebViewImpl::Reload() {
163 base::DictionaryValue params;
164 params.SetBoolean("ignoreCache", false);
165 return client_->SendCommand("Page.reload", params);
166 }
167
EvaluateScript(const std::string & frame,const std::string & expression,scoped_ptr<base::Value> * result)168 Status WebViewImpl::EvaluateScript(const std::string& frame,
169 const std::string& expression,
170 scoped_ptr<base::Value>* result) {
171 int context_id;
172 Status status = GetContextIdForFrame(frame_tracker_.get(), frame,
173 &context_id);
174 if (status.IsError())
175 return status;
176 return internal::EvaluateScriptAndGetValue(
177 client_.get(), context_id, expression, result);
178 }
179
CallFunction(const std::string & frame,const std::string & function,const base::ListValue & args,scoped_ptr<base::Value> * result)180 Status WebViewImpl::CallFunction(const std::string& frame,
181 const std::string& function,
182 const base::ListValue& args,
183 scoped_ptr<base::Value>* result) {
184 std::string json;
185 base::JSONWriter::Write(&args, &json);
186 // TODO(zachconrad): Second null should be array of shadow host ids.
187 std::string expression = base::StringPrintf(
188 "(%s).apply(null, [null, %s, %s])",
189 kCallFunctionScript,
190 function.c_str(),
191 json.c_str());
192 scoped_ptr<base::Value> temp_result;
193 Status status = EvaluateScript(frame, expression, &temp_result);
194 if (status.IsError())
195 return status;
196
197 return internal::ParseCallFunctionResult(*temp_result, result);
198 }
199
CallAsyncFunction(const std::string & frame,const std::string & function,const base::ListValue & args,const base::TimeDelta & timeout,scoped_ptr<base::Value> * result)200 Status WebViewImpl::CallAsyncFunction(const std::string& frame,
201 const std::string& function,
202 const base::ListValue& args,
203 const base::TimeDelta& timeout,
204 scoped_ptr<base::Value>* result) {
205 return CallAsyncFunctionInternal(
206 frame, function, args, false, timeout, result);
207 }
208
CallUserAsyncFunction(const std::string & frame,const std::string & function,const base::ListValue & args,const base::TimeDelta & timeout,scoped_ptr<base::Value> * result)209 Status WebViewImpl::CallUserAsyncFunction(const std::string& frame,
210 const std::string& function,
211 const base::ListValue& args,
212 const base::TimeDelta& timeout,
213 scoped_ptr<base::Value>* result) {
214 return CallAsyncFunctionInternal(
215 frame, function, args, true, timeout, result);
216 }
217
GetFrameByFunction(const std::string & frame,const std::string & function,const base::ListValue & args,std::string * out_frame)218 Status WebViewImpl::GetFrameByFunction(const std::string& frame,
219 const std::string& function,
220 const base::ListValue& args,
221 std::string* out_frame) {
222 int context_id;
223 Status status = GetContextIdForFrame(frame_tracker_.get(), frame,
224 &context_id);
225 if (status.IsError())
226 return status;
227 bool found_node;
228 int node_id;
229 status = internal::GetNodeIdFromFunction(
230 client_.get(), context_id, function, args, &found_node, &node_id);
231 if (status.IsError())
232 return status;
233 if (!found_node)
234 return Status(kNoSuchFrame);
235 return dom_tracker_->GetFrameIdForNode(node_id, out_frame);
236 }
237
DispatchMouseEvents(const std::list<MouseEvent> & events,const std::string & frame)238 Status WebViewImpl::DispatchMouseEvents(const std::list<MouseEvent>& events,
239 const std::string& frame) {
240 for (std::list<MouseEvent>::const_iterator it = events.begin();
241 it != events.end(); ++it) {
242 base::DictionaryValue params;
243 params.SetString("type", GetAsString(it->type));
244 params.SetInteger("x", it->x);
245 params.SetInteger("y", it->y);
246 params.SetInteger("modifiers", it->modifiers);
247 params.SetString("button", GetAsString(it->button));
248 params.SetInteger("clickCount", it->click_count);
249 Status status = client_->SendCommand("Input.dispatchMouseEvent", params);
250 if (status.IsError())
251 return status;
252 if (browser_info_->build_no < 1569 && it->button == kRightMouseButton &&
253 it->type == kReleasedMouseEventType) {
254 base::ListValue args;
255 args.AppendInteger(it->x);
256 args.AppendInteger(it->y);
257 args.AppendInteger(it->modifiers);
258 scoped_ptr<base::Value> result;
259 status = CallFunction(
260 frame, kDispatchContextMenuEventScript, args, &result);
261 if (status.IsError())
262 return status;
263 }
264 }
265 return Status(kOk);
266 }
267
DispatchTouchEvent(const TouchEvent & event)268 Status WebViewImpl::DispatchTouchEvent(const TouchEvent& event) {
269 base::DictionaryValue params;
270 params.SetString("type", GetAsString(event.type));
271 scoped_ptr<base::ListValue> point_list(new base::ListValue);
272 scoped_ptr<base::DictionaryValue> point(new base::DictionaryValue);
273 point->SetString("state", GetPointStateString(event.type));
274 point->SetInteger("x", event.x);
275 point->SetInteger("y", event.y);
276 point_list->Set(0, point.release());
277 params.Set("touchPoints", point_list.release());
278 return client_->SendCommand("Input.dispatchTouchEvent", params);
279 }
280
DispatchTouchEvents(const std::list<TouchEvent> & events)281 Status WebViewImpl::DispatchTouchEvents(const std::list<TouchEvent>& events) {
282 for (std::list<TouchEvent>::const_iterator it = events.begin();
283 it != events.end(); ++it) {
284 Status status = DispatchTouchEvent(*it);
285 if (status.IsError())
286 return status;
287 }
288 return Status(kOk);
289 }
290
DispatchKeyEvents(const std::list<KeyEvent> & events)291 Status WebViewImpl::DispatchKeyEvents(const std::list<KeyEvent>& events) {
292 for (std::list<KeyEvent>::const_iterator it = events.begin();
293 it != events.end(); ++it) {
294 base::DictionaryValue params;
295 params.SetString("type", GetAsString(it->type));
296 if (it->modifiers & kNumLockKeyModifierMask) {
297 params.SetBoolean("isKeypad", true);
298 params.SetInteger("modifiers",
299 it->modifiers & ~kNumLockKeyModifierMask);
300 } else {
301 params.SetInteger("modifiers", it->modifiers);
302 }
303 params.SetString("text", it->modified_text);
304 params.SetString("unmodifiedText", it->unmodified_text);
305 params.SetInteger("nativeVirtualKeyCode", it->key_code);
306 params.SetInteger("windowsVirtualKeyCode", it->key_code);
307 Status status = client_->SendCommand("Input.dispatchKeyEvent", params);
308 if (status.IsError())
309 return status;
310 }
311 return Status(kOk);
312 }
313
GetCookies(scoped_ptr<base::ListValue> * cookies)314 Status WebViewImpl::GetCookies(scoped_ptr<base::ListValue>* cookies) {
315 base::DictionaryValue params;
316 scoped_ptr<base::DictionaryValue> result;
317 Status status = client_->SendCommandAndGetResult(
318 "Page.getCookies", params, &result);
319 if (status.IsError())
320 return status;
321 base::ListValue* cookies_tmp;
322 if (!result->GetList("cookies", &cookies_tmp))
323 return Status(kUnknownError, "DevTools didn't return cookies");
324 cookies->reset(cookies_tmp->DeepCopy());
325 return Status(kOk);
326 }
327
DeleteCookie(const std::string & name,const std::string & url)328 Status WebViewImpl::DeleteCookie(const std::string& name,
329 const std::string& url) {
330 base::DictionaryValue params;
331 params.SetString("cookieName", name);
332 params.SetString("url", url);
333 return client_->SendCommand("Page.deleteCookie", params);
334 }
335
WaitForPendingNavigations(const std::string & frame_id,const base::TimeDelta & timeout,bool stop_load_on_timeout)336 Status WebViewImpl::WaitForPendingNavigations(const std::string& frame_id,
337 const base::TimeDelta& timeout,
338 bool stop_load_on_timeout) {
339 VLOG(0) << "Waiting for pending navigations...";
340 Status status = client_->HandleEventsUntil(
341 base::Bind(&WebViewImpl::IsNotPendingNavigation,
342 base::Unretained(this),
343 frame_id),
344 timeout);
345 if (status.code() == kTimeout && stop_load_on_timeout) {
346 VLOG(0) << "Timed out. Stopping navigation...";
347 scoped_ptr<base::Value> unused_value;
348 EvaluateScript(std::string(), "window.stop();", &unused_value);
349 Status new_status = client_->HandleEventsUntil(
350 base::Bind(&WebViewImpl::IsNotPendingNavigation, base::Unretained(this),
351 frame_id),
352 base::TimeDelta::FromSeconds(10));
353 if (new_status.IsError())
354 status = new_status;
355 }
356 VLOG(0) << "Done waiting for pending navigations";
357 return status;
358 }
359
IsPendingNavigation(const std::string & frame_id,bool * is_pending)360 Status WebViewImpl::IsPendingNavigation(const std::string& frame_id,
361 bool* is_pending) {
362 return navigation_tracker_->IsPendingNavigation(frame_id, is_pending);
363 }
364
GetJavaScriptDialogManager()365 JavaScriptDialogManager* WebViewImpl::GetJavaScriptDialogManager() {
366 return dialog_manager_.get();
367 }
368
OverrideGeolocation(const Geoposition & geoposition)369 Status WebViewImpl::OverrideGeolocation(const Geoposition& geoposition) {
370 return geolocation_override_manager_->OverrideGeolocation(geoposition);
371 }
372
CaptureScreenshot(std::string * screenshot)373 Status WebViewImpl::CaptureScreenshot(std::string* screenshot) {
374 base::DictionaryValue params;
375 scoped_ptr<base::DictionaryValue> result;
376 Status status = client_->SendCommandAndGetResult(
377 "Page.captureScreenshot", params, &result);
378 if (status.IsError())
379 return status;
380 if (!result->GetString("data", screenshot))
381 return Status(kUnknownError, "expected string 'data' in response");
382 return Status(kOk);
383 }
384
SetFileInputFiles(const std::string & frame,const base::DictionaryValue & element,const std::vector<base::FilePath> & files)385 Status WebViewImpl::SetFileInputFiles(
386 const std::string& frame,
387 const base::DictionaryValue& element,
388 const std::vector<base::FilePath>& files) {
389 base::ListValue file_list;
390 for (size_t i = 0; i < files.size(); ++i) {
391 if (!files[i].IsAbsolute()) {
392 return Status(kUnknownError,
393 "path is not absolute: " + files[i].AsUTF8Unsafe());
394 }
395 if (files[i].ReferencesParent()) {
396 return Status(kUnknownError,
397 "path is not canonical: " + files[i].AsUTF8Unsafe());
398 }
399 file_list.AppendString(files[i].value());
400 }
401
402 int context_id;
403 Status status = GetContextIdForFrame(frame_tracker_.get(), frame,
404 &context_id);
405 if (status.IsError())
406 return status;
407 base::ListValue args;
408 args.Append(element.DeepCopy());
409 bool found_node;
410 int node_id;
411 status = internal::GetNodeIdFromFunction(
412 client_.get(), context_id, "function(element) { return element; }",
413 args, &found_node, &node_id);
414 if (status.IsError())
415 return status;
416 if (!found_node)
417 return Status(kUnknownError, "no node ID for file input");
418 base::DictionaryValue params;
419 params.SetInteger("nodeId", node_id);
420 params.Set("files", file_list.DeepCopy());
421 return client_->SendCommand("DOM.setFileInputFiles", params);
422 }
423
TakeHeapSnapshot(scoped_ptr<base::Value> * snapshot)424 Status WebViewImpl::TakeHeapSnapshot(scoped_ptr<base::Value>* snapshot) {
425 return heap_snapshot_taker_->TakeSnapshot(snapshot);
426 }
427
InitProfileInternal()428 Status WebViewImpl::InitProfileInternal() {
429 base::DictionaryValue params;
430
431 // TODO: Remove Debugger.enable after Chrome 36 stable is released.
432 Status status_debug = client_->SendCommand("Debugger.enable", params);
433
434 if (status_debug.IsError())
435 return status_debug;
436
437 Status status_profiler = client_->SendCommand("Profiler.enable", params);
438
439 if (status_profiler.IsError()) {
440 Status status_debugger = client_->SendCommand("Debugger.disable", params);
441 if (status_debugger.IsError())
442 return status_debugger;
443
444 return status_profiler;
445 }
446
447 return Status(kOk);
448 }
449
StopProfileInternal()450 Status WebViewImpl::StopProfileInternal() {
451 base::DictionaryValue params;
452 Status status_debug = client_->SendCommand("Debugger.disable", params);
453 Status status_profiler = client_->SendCommand("Profiler.disable", params);
454
455 if (status_debug.IsError()) {
456 return status_debug;
457 } else if (status_profiler.IsError()) {
458 return status_profiler;
459 }
460
461 return Status(kOk);
462 }
463
StartProfile()464 Status WebViewImpl::StartProfile() {
465 Status status_init = InitProfileInternal();
466
467 if (status_init.IsError())
468 return status_init;
469
470 base::DictionaryValue params;
471 return client_->SendCommand("Profiler.start", params);
472 }
473
EndProfile(scoped_ptr<base::Value> * profile_data)474 Status WebViewImpl::EndProfile(scoped_ptr<base::Value>* profile_data) {
475 base::DictionaryValue params;
476 scoped_ptr<base::DictionaryValue> profile_result;
477
478 Status status = client_->SendCommandAndGetResult(
479 "Profiler.stop", params, &profile_result);
480
481 if (status.IsError()) {
482 Status disable_profile_status = StopProfileInternal();
483 if (disable_profile_status.IsError()) {
484 return disable_profile_status;
485 } else {
486 return status;
487 }
488 }
489
490 *profile_data = profile_result.PassAs<base::Value>();
491 return status;
492 }
493
CallAsyncFunctionInternal(const std::string & frame,const std::string & function,const base::ListValue & args,bool is_user_supplied,const base::TimeDelta & timeout,scoped_ptr<base::Value> * result)494 Status WebViewImpl::CallAsyncFunctionInternal(const std::string& frame,
495 const std::string& function,
496 const base::ListValue& args,
497 bool is_user_supplied,
498 const base::TimeDelta& timeout,
499 scoped_ptr<base::Value>* result) {
500 base::ListValue async_args;
501 async_args.AppendString("return (" + function + ").apply(null, arguments);");
502 async_args.Append(args.DeepCopy());
503 async_args.AppendBoolean(is_user_supplied);
504 async_args.AppendInteger(timeout.InMilliseconds());
505 scoped_ptr<base::Value> tmp;
506 Status status = CallFunction(
507 frame, kExecuteAsyncScriptScript, async_args, &tmp);
508 if (status.IsError())
509 return status;
510
511 const char* kDocUnloadError = "document unloaded while waiting for result";
512 std::string kQueryResult = base::StringPrintf(
513 "function() {"
514 " var info = document.$chrome_asyncScriptInfo;"
515 " if (!info)"
516 " return {status: %d, value: '%s'};"
517 " var result = info.result;"
518 " if (!result)"
519 " return {status: 0};"
520 " delete info.result;"
521 " return result;"
522 "}",
523 kJavaScriptError,
524 kDocUnloadError);
525
526 while (true) {
527 base::ListValue no_args;
528 scoped_ptr<base::Value> query_value;
529 Status status = CallFunction(frame, kQueryResult, no_args, &query_value);
530 if (status.IsError()) {
531 if (status.code() == kNoSuchFrame)
532 return Status(kJavaScriptError, kDocUnloadError);
533 return status;
534 }
535
536 base::DictionaryValue* result_info = NULL;
537 if (!query_value->GetAsDictionary(&result_info))
538 return Status(kUnknownError, "async result info is not a dictionary");
539 int status_code;
540 if (!result_info->GetInteger("status", &status_code))
541 return Status(kUnknownError, "async result info has no int 'status'");
542 if (status_code != kOk) {
543 std::string message;
544 result_info->GetString("value", &message);
545 return Status(static_cast<StatusCode>(status_code), message);
546 }
547
548 base::Value* value = NULL;
549 if (result_info->Get("value", &value)) {
550 result->reset(value->DeepCopy());
551 return Status(kOk);
552 }
553
554 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
555 }
556 }
557
IsNotPendingNavigation(const std::string & frame_id,bool * is_not_pending)558 Status WebViewImpl::IsNotPendingNavigation(const std::string& frame_id,
559 bool* is_not_pending) {
560 bool is_pending;
561 Status status =
562 navigation_tracker_->IsPendingNavigation(frame_id, &is_pending);
563 if (status.IsError())
564 return status;
565 // An alert may block the pending navigation.
566 if (is_pending && dialog_manager_->IsDialogOpen())
567 return Status(kUnexpectedAlertOpen);
568
569 *is_not_pending = !is_pending;
570 return Status(kOk);
571 }
572
573 namespace internal {
574
EvaluateScript(DevToolsClient * client,int context_id,const std::string & expression,EvaluateScriptReturnType return_type,scoped_ptr<base::DictionaryValue> * result)575 Status EvaluateScript(DevToolsClient* client,
576 int context_id,
577 const std::string& expression,
578 EvaluateScriptReturnType return_type,
579 scoped_ptr<base::DictionaryValue>* result) {
580 base::DictionaryValue params;
581 params.SetString("expression", expression);
582 if (context_id)
583 params.SetInteger("contextId", context_id);
584 params.SetBoolean("returnByValue", return_type == ReturnByValue);
585 scoped_ptr<base::DictionaryValue> cmd_result;
586 Status status = client->SendCommandAndGetResult(
587 "Runtime.evaluate", params, &cmd_result);
588 if (status.IsError())
589 return status;
590
591 bool was_thrown;
592 if (!cmd_result->GetBoolean("wasThrown", &was_thrown))
593 return Status(kUnknownError, "Runtime.evaluate missing 'wasThrown'");
594 if (was_thrown) {
595 std::string description = "unknown";
596 cmd_result->GetString("result.description", &description);
597 return Status(kUnknownError,
598 "Runtime.evaluate threw exception: " + description);
599 }
600
601 base::DictionaryValue* unscoped_result;
602 if (!cmd_result->GetDictionary("result", &unscoped_result))
603 return Status(kUnknownError, "evaluate missing dictionary 'result'");
604 result->reset(unscoped_result->DeepCopy());
605 return Status(kOk);
606 }
607
EvaluateScriptAndGetObject(DevToolsClient * client,int context_id,const std::string & expression,bool * got_object,std::string * object_id)608 Status EvaluateScriptAndGetObject(DevToolsClient* client,
609 int context_id,
610 const std::string& expression,
611 bool* got_object,
612 std::string* object_id) {
613 scoped_ptr<base::DictionaryValue> result;
614 Status status = EvaluateScript(client, context_id, expression, ReturnByObject,
615 &result);
616 if (status.IsError())
617 return status;
618 if (!result->HasKey("objectId")) {
619 *got_object = false;
620 return Status(kOk);
621 }
622 if (!result->GetString("objectId", object_id))
623 return Status(kUnknownError, "evaluate has invalid 'objectId'");
624 *got_object = true;
625 return Status(kOk);
626 }
627
EvaluateScriptAndGetValue(DevToolsClient * client,int context_id,const std::string & expression,scoped_ptr<base::Value> * result)628 Status EvaluateScriptAndGetValue(DevToolsClient* client,
629 int context_id,
630 const std::string& expression,
631 scoped_ptr<base::Value>* result) {
632 scoped_ptr<base::DictionaryValue> temp_result;
633 Status status = EvaluateScript(client, context_id, expression, ReturnByValue,
634 &temp_result);
635 if (status.IsError())
636 return status;
637
638 std::string type;
639 if (!temp_result->GetString("type", &type))
640 return Status(kUnknownError, "Runtime.evaluate missing string 'type'");
641
642 if (type == "undefined") {
643 result->reset(base::Value::CreateNullValue());
644 } else {
645 base::Value* value;
646 if (!temp_result->Get("value", &value))
647 return Status(kUnknownError, "Runtime.evaluate missing 'value'");
648 result->reset(value->DeepCopy());
649 }
650 return Status(kOk);
651 }
652
ParseCallFunctionResult(const base::Value & temp_result,scoped_ptr<base::Value> * result)653 Status ParseCallFunctionResult(const base::Value& temp_result,
654 scoped_ptr<base::Value>* result) {
655 const base::DictionaryValue* dict;
656 if (!temp_result.GetAsDictionary(&dict))
657 return Status(kUnknownError, "call function result must be a dictionary");
658 int status_code;
659 if (!dict->GetInteger("status", &status_code)) {
660 return Status(kUnknownError,
661 "call function result missing int 'status'");
662 }
663 if (status_code != kOk) {
664 std::string message;
665 dict->GetString("value", &message);
666 return Status(static_cast<StatusCode>(status_code), message);
667 }
668 const base::Value* unscoped_value;
669 if (!dict->Get("value", &unscoped_value)) {
670 return Status(kUnknownError,
671 "call function result missing 'value'");
672 }
673 result->reset(unscoped_value->DeepCopy());
674 return Status(kOk);
675 }
676
GetNodeIdFromFunction(DevToolsClient * client,int context_id,const std::string & function,const base::ListValue & args,bool * found_node,int * node_id)677 Status GetNodeIdFromFunction(DevToolsClient* client,
678 int context_id,
679 const std::string& function,
680 const base::ListValue& args,
681 bool* found_node,
682 int* node_id) {
683 std::string json;
684 base::JSONWriter::Write(&args, &json);
685 // TODO(zachconrad): Second null should be array of shadow host ids.
686 std::string expression = base::StringPrintf(
687 "(%s).apply(null, [null, %s, %s, true])",
688 kCallFunctionScript,
689 function.c_str(),
690 json.c_str());
691
692 bool got_object;
693 std::string element_id;
694 Status status = internal::EvaluateScriptAndGetObject(
695 client, context_id, expression, &got_object, &element_id);
696 if (status.IsError())
697 return status;
698 if (!got_object) {
699 *found_node = false;
700 return Status(kOk);
701 }
702
703 scoped_ptr<base::DictionaryValue> cmd_result;
704 {
705 base::DictionaryValue params;
706 params.SetString("objectId", element_id);
707 status = client->SendCommandAndGetResult(
708 "DOM.requestNode", params, &cmd_result);
709 }
710 {
711 // Release the remote object before doing anything else.
712 base::DictionaryValue params;
713 params.SetString("objectId", element_id);
714 Status release_status =
715 client->SendCommand("Runtime.releaseObject", params);
716 if (release_status.IsError()) {
717 LOG(ERROR) << "Failed to release remote object: "
718 << release_status.message();
719 }
720 }
721 if (status.IsError())
722 return status;
723
724 if (!cmd_result->GetInteger("nodeId", node_id))
725 return Status(kUnknownError, "DOM.requestNode missing int 'nodeId'");
726 *found_node = true;
727 return Status(kOk);
728 }
729
730 } // namespace internal
731