• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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