1// Copyright (c) 2012 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/dom_tracker.h"
6
7#include <utility>
8
9#include "base/json/json_writer.h"
10#include "base/values.h"
11#include "chrome/test/chromedriver/chrome/devtools_client.h"
12#include "chrome/test/chromedriver/chrome/status.h"
13
14DomTracker::DomTracker(DevToolsClient* client) {
15  client->AddListener(this);
16}
17
18DomTracker::~DomTracker() {}
19
20Status DomTracker::GetFrameIdForNode(
21    int node_id, std::string* frame_id) {
22  if (node_to_frame_map_.count(node_id) == 0)
23    return Status(kNoSuchFrame, "element is not a frame");
24  *frame_id = node_to_frame_map_[node_id];
25  return Status(kOk);
26}
27
28Status DomTracker::OnConnected(DevToolsClient* client) {
29  node_to_frame_map_.clear();
30  // Fetch the root document node so that Inspector will push DOM node
31  // information to the client.
32  base::DictionaryValue params;
33  return client->SendCommand("DOM.getDocument", params);
34}
35
36Status DomTracker::OnEvent(DevToolsClient* client,
37                           const std::string& method,
38                           const base::DictionaryValue& params) {
39  if (method == "DOM.setChildNodes") {
40    const base::Value* nodes;
41    if (!params.Get("nodes", &nodes))
42      return Status(kUnknownError, "DOM.setChildNodes missing 'nodes'");
43
44    if (!ProcessNodeList(nodes)) {
45      std::string json;
46      base::JSONWriter::Write(nodes, &json);
47      return Status(kUnknownError,
48                    "DOM.setChildNodes has invalid 'nodes': " + json);
49    }
50  } else if (method == "DOM.childNodeInserted") {
51    const base::Value* node;
52    if (!params.Get("node", &node))
53      return Status(kUnknownError, "DOM.childNodeInserted missing 'node'");
54
55    if (!ProcessNode(node)) {
56      std::string json;
57      base::JSONWriter::Write(node, &json);
58      return Status(kUnknownError,
59                    "DOM.childNodeInserted has invalid 'node': " + json);
60    }
61  } else if (method == "DOM.documentUpdated") {
62    node_to_frame_map_.clear();
63    base::DictionaryValue params;
64    client->SendCommand("DOM.getDocument", params);
65  }
66  return Status(kOk);
67}
68
69bool DomTracker::ProcessNodeList(const base::Value* nodes) {
70  const base::ListValue* nodes_list;
71  if (!nodes->GetAsList(&nodes_list))
72    return false;
73  for (size_t i = 0; i < nodes_list->GetSize(); ++i) {
74    const base::Value* node;
75    if (!nodes_list->Get(i, &node))
76      return false;
77    if (!ProcessNode(node))
78      return false;
79  }
80  return true;
81}
82
83bool DomTracker::ProcessNode(const base::Value* node) {
84  const base::DictionaryValue* dict;
85  if (!node->GetAsDictionary(&dict))
86    return false;
87  int node_id;
88  if (!dict->GetInteger("nodeId", &node_id))
89    return false;
90  std::string frame_id;
91  if (dict->GetString("frameId", &frame_id))
92    node_to_frame_map_.insert(std::make_pair(node_id, frame_id));
93
94  const base::Value* children;
95  if (dict->Get("children", &children))
96    return ProcessNodeList(children);
97  return true;
98}
99