• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that
3 // can be found in the LICENSE file.
4 
5 #include "libcef/browser/devtools/devtools_util.h"
6 
7 #include "base/strings/string_number_conversions.h"
8 #include "base/strings/string_util.h"
9 
10 namespace devtools_util {
11 
12 namespace {
13 
IsValidDictionary(const base::StringPiece & str,bool allow_empty)14 bool IsValidDictionary(const base::StringPiece& str, bool allow_empty) {
15   return str.length() >= (allow_empty ? 2 : 3) && str[0] == '{' &&
16          str[str.length() - 1] == '}';
17 }
18 
19 // Example:
20 // {"method":"Target.targetDestroyed","params":{"targetId":"1234..."}}
ParseEvent(const base::StringPiece & message,base::StringPiece & method,base::StringPiece & params)21 bool ParseEvent(const base::StringPiece& message,
22                 base::StringPiece& method,
23                 base::StringPiece& params) {
24   static const char kMethodStart[] = "{\"method\":\"";
25   static const char kMethodEnd[] = "\"";
26   static const char kParamsStart[] = ",\"params\":";
27 
28   if (!base::StartsWith(message, kMethodStart))
29     return false;
30 
31   const size_t method_start = sizeof(kMethodStart) - 1;
32   const size_t method_end = message.find(kMethodEnd, method_start);
33   if (method_end < 0U)
34     return false;
35   method = message.substr(method_start, method_end - method_start);
36   if (method.empty())
37     return false;
38 
39   size_t remainder_start = method_end + sizeof(kMethodEnd) - 1;
40   if (remainder_start == message.size() - 1) {
41     // No more contents.
42     params = base::StringPiece();
43   } else {
44     const base::StringPiece& remainder = message.substr(remainder_start);
45     if (base::StartsWith(remainder, kParamsStart)) {
46       // Stop immediately before the message closing bracket.
47       remainder_start += sizeof(kParamsStart) - 1;
48       params =
49           message.substr(remainder_start, message.size() - 1 - remainder_start);
50     } else {
51       // Invalid format.
52       return false;
53     }
54 
55     if (!IsValidDictionary(params, /*allow_empty=*/true))
56       return false;
57   }
58 
59   return true;
60 }
61 
62 // Examples:
63 // {"id":3,"result":{}}
64 // {"id":4,"result":{"debuggerId":"-2193881606781505058.81393575456727957"}}
65 // {"id":5,"error":{"code":-32000,"message":"Not supported"}}
ParseResult(const base::StringPiece & message,int & message_id,bool & success,base::StringPiece & result)66 bool ParseResult(const base::StringPiece& message,
67                  int& message_id,
68                  bool& success,
69                  base::StringPiece& result) {
70   static const char kIdStart[] = "{\"id\":";
71   static const char kIdEnd[] = ",";
72   static const char kResultStart[] = "\"result\":";
73   static const char kErrorStart[] = "\"error\":";
74 
75   if (!base::StartsWith(message, kIdStart))
76     return false;
77 
78   const size_t id_start = sizeof(kIdStart) - 1;
79   const size_t id_end = message.find(kIdEnd, id_start);
80   if (id_end < 0U)
81     return false;
82   const base::StringPiece& id_str = message.substr(id_start, id_end - id_start);
83   if (id_str.empty() || !base::StringToInt(id_str, &message_id))
84     return false;
85 
86   size_t remainder_start = id_end + sizeof(kIdEnd) - 1;
87   const base::StringPiece& remainder = message.substr(remainder_start);
88   if (base::StartsWith(remainder, kResultStart)) {
89     // Stop immediately before the message closing bracket.
90     remainder_start += sizeof(kResultStart) - 1;
91     result =
92         message.substr(remainder_start, message.size() - 1 - remainder_start);
93     success = true;
94   } else if (base::StartsWith(remainder, kErrorStart)) {
95     // Stop immediately before the message closing bracket.
96     remainder_start += sizeof(kErrorStart) - 1;
97     result =
98         message.substr(remainder_start, message.size() - 1 - remainder_start);
99     success = false;
100   } else {
101     // Invalid format.
102     return false;
103   }
104 
105   if (!IsValidDictionary(result, /*allow_empty=*/true))
106     return false;
107 
108   return true;
109 }
110 
111 }  // namespace
112 
113 // static
IsValidMessage(const base::StringPiece & message)114 bool ProtocolParser::IsValidMessage(const base::StringPiece& message) {
115   return IsValidDictionary(message, /*allow_empty=*/false);
116 }
117 
Initialize(const base::StringPiece & message)118 bool ProtocolParser::Initialize(const base::StringPiece& message) {
119   if (status_ != UNINITIALIZED)
120     return false;
121 
122   if (ParseEvent(message, method_, params_)) {
123     status_ = EVENT;
124   } else if (ParseResult(message, message_id_, success_, params_)) {
125     status_ = RESULT;
126   } else {
127     status_ = FAILURE;
128   }
129   return true;
130 }
131 
132 }  // namespace devtools_util
133