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/browser_info.h"
6
7#include "base/json/json_reader.h"
8#include "base/memory/scoped_ptr.h"
9#include "base/strings/string_number_conversions.h"
10#include "base/strings/string_split.h"
11#include "base/strings/string_util.h"
12#include "base/values.h"
13
14BrowserInfo::BrowserInfo()
15    : browser_name(std::string()),
16      browser_version(std::string()),
17      build_no(kToTBuildNo),
18      blink_revision(kToTBlinkRevision) {
19}
20
21BrowserInfo::BrowserInfo(std::string browser_name,
22                         std::string browser_version,
23                         int build_no,
24                         int blink_revision)
25    : browser_name(browser_name),
26      browser_version(browser_version),
27      build_no(build_no),
28      blink_revision(blink_revision) {
29}
30
31Status ParseBrowserInfo(const std::string& data, BrowserInfo* browser_info) {
32  scoped_ptr<base::Value> value(base::JSONReader::Read(data));
33  if (!value.get())
34    return Status(kUnknownError, "version info not in JSON");
35
36  base::DictionaryValue* dict;
37  if (!value->GetAsDictionary(&dict))
38    return Status(kUnknownError, "version info not a dictionary");
39
40  std::string browser;
41  if (!dict->GetString("Browser", &browser)) {
42    return Status(kUnknownError,
43                  "version info doesn't include string 'Browser'");
44  }
45
46  std::string blink_version;
47  if (!dict->GetString("WebKit-Version", &blink_version)) {
48    return Status(kUnknownError,
49                  "version info doesn't include string 'WebKit-Version'");
50  }
51
52  Status status = ParseBrowserString(browser, &browser_info->browser_name,
53      &browser_info->browser_version, &browser_info->build_no);
54
55  if (status.IsError())
56    return status;
57
58  return ParseBlinkVersionString(blink_version, &browser_info->blink_revision);
59}
60
61Status ParseBrowserString(const std::string& browser_string,
62                          std::string* browser_name,
63                          std::string* browser_version,
64                          int* build_no) {
65  if (browser_string.empty()) {
66    *browser_name = "content shell";
67    return Status(kOk);
68  }
69
70  if (browser_string.find("Version/") == 0u) {
71    *browser_name = "webview";
72    return Status(kOk);
73  }
74
75  std::string prefix = "Chrome/";
76  if (browser_string.find(prefix) == 0u) {
77    *browser_name = "chrome";
78    *browser_version = browser_string.substr(prefix.length());
79
80    std::vector<std::string> version_parts;
81    base::SplitString(*browser_version, '.', &version_parts);
82    if (version_parts.size() != 4 ||
83        !base::StringToInt(version_parts[2], build_no)) {
84      return Status(kUnknownError,
85                    "unrecognized Chrome version: " + *browser_version);
86    }
87
88    return Status(kOk);
89  }
90
91  return Status(kUnknownError,
92                "unrecognized Chrome version: " + browser_string);
93}
94
95Status ParseBlinkVersionString(const std::string& blink_version,
96                               int* blink_revision) {
97  size_t before = blink_version.find('@');
98  size_t after = blink_version.find(')');
99  if (before == std::string::npos || after == std::string::npos) {
100    return Status(kUnknownError,
101                  "unrecognized Blink version string: " + blink_version);
102  }
103
104  // Chrome OS reports its Blink revision as a git hash. In this case, ignore it
105  // and don't set |blink_revision|. For Chrome (and for Chrome OS) we use the
106  // build number instead of the blink revision for decisions about backwards
107  // compatibility.
108  std::string revision = blink_version.substr(before + 1, after - before - 1);
109  if (!IsGitHash(revision) && !base::StringToInt(revision, blink_revision)) {
110    return Status(kUnknownError, "unrecognized Blink revision: " + revision);
111  }
112
113  return Status(kOk);
114}
115
116bool IsGitHash(const std::string& revision) {
117  const int kShortGitHashLength = 7;
118  const int kFullGitHashLength = 40;
119  return kShortGitHashLength <= revision.size()
120      && revision.size() <= kFullGitHashLength
121      && base::ContainsOnlyChars(revision, "0123456789abcdefABCDEF");
122}
123