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/console_logger.h"
6
7#include "base/json/json_writer.h"
8#include "base/logging.h"
9#include "base/strings/stringprintf.h"
10#include "base/values.h"
11#include "chrome/test/chromedriver/chrome/devtools_client.h"
12#include "chrome/test/chromedriver/chrome/log.h"
13#include "chrome/test/chromedriver/chrome/status.h"
14
15namespace {
16
17// Translates Console.messageAdded.message.level into Log::Level.
18bool ConsoleLevelToLogLevel(const std::string& name, Log::Level *out_level) {
19  const char* const kConsoleLevelNames[] = {
20    "debug", "log", "warning", "error"
21  };
22
23  for (size_t i = 0; i < arraysize(kConsoleLevelNames); ++i) {
24    if (name == kConsoleLevelNames[i]) {
25      CHECK_LE(Log::kDebug + i, static_cast<size_t>(Log::kError));
26      *out_level = static_cast<Log::Level>(Log::kDebug + i);
27      return true;
28    }
29  }
30  return false;
31}
32
33}  // namespace
34
35ConsoleLogger::ConsoleLogger(Log* log)
36    : log_(log) {}
37
38Status ConsoleLogger::OnConnected(DevToolsClient* client) {
39  base::DictionaryValue params;
40  return client->SendCommand("Console.enable", params);
41}
42
43Status ConsoleLogger::OnEvent(
44    DevToolsClient* client,
45    const std::string& method,
46    const base::DictionaryValue& params) {
47  if (method != "Console.messageAdded")
48    return Status(kOk);
49
50  // If the event has proper structure and fields, log formatted.
51  // Else it's a weird message that we don't know how to format, log full JSON.
52  const base::DictionaryValue *message_dict = NULL;
53  if (params.GetDictionary("message", &message_dict)) {
54    std::string text;
55    std::string level_name;
56    Log::Level level = Log::kInfo;
57    if (message_dict->GetString("text", &text) && !text.empty() &&
58        message_dict->GetString("level", &level_name) &&
59        ConsoleLevelToLogLevel(level_name, &level)) {
60
61      const char* origin_cstr = "unknown";
62      std::string origin;
63      if ((message_dict->GetString("url", &origin) && !origin.empty()) ||
64          (message_dict->GetString("source", &origin) && !origin.empty())) {
65        origin_cstr = origin.c_str();
66      }
67
68      std::string line_column;
69      int line = -1;
70      if (message_dict->GetInteger("line", &line)) {
71        int column = -1;
72        if (message_dict->GetInteger("column", &column)) {
73          base::SStringPrintf(&line_column, "%d:%d", line, column);
74        } else {
75          base::SStringPrintf(&line_column, "%d", line);
76        }
77      } else {
78        // No line number, but print anyway, just to maintain the number of
79        // fields in the formatted message in case someone wants to parse it.
80        line_column = "-";
81      }
82
83      std::string source;
84      message_dict->GetString("source", &source);
85      log_->AddEntry(level, source, base::StringPrintf("%s %s %s",
86                                                       origin_cstr,
87                                                       line_column.c_str(),
88                                                       text.c_str()));
89
90      return Status(kOk);
91    }
92  }
93
94  // Don't know how to format, log full JSON.
95  std::string message_json;
96  base::JSONWriter::Write(&params, &message_json);
97  log_->AddEntry(Log::kWarning, message_json);
98  return Status(kOk);
99}
100