File: declarative_event.cc

package info (click to toggle)
chromium 138.0.7204.183-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,908 kB
  • sloc: cpp: 34,937,088; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (210 lines) | stat: -rw-r--r-- 8,821 bytes parent folder | download | duplicates (9)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "extensions/renderer/bindings/declarative_event.h"

#include <algorithm>
#include <memory>

#include "base/values.h"
#include "extensions/renderer/bindings/api_event_listeners.h"
#include "extensions/renderer/bindings/api_request_handler.h"
#include "extensions/renderer/bindings/api_signature.h"
#include "extensions/renderer/bindings/api_type_reference_map.h"
#include "extensions/renderer/bindings/argument_spec.h"
#include "gin/object_template_builder.h"
#include "gin/per_context_data.h"

namespace extensions {

namespace {

// Builds an ArgumentSpec that accepts the given |choices| as references.
std::unique_ptr<ArgumentSpec> BuildChoicesSpec(
    const std::vector<std::string>& choices_list) {
  auto item_type = std::make_unique<ArgumentSpec>(ArgumentType::CHOICES);
  std::vector<std::unique_ptr<ArgumentSpec>> choices;
  choices.reserve(choices_list.size());
  for (const std::string& value : choices_list) {
    auto choice = std::make_unique<ArgumentSpec>(ArgumentType::REF);
    choice->set_ref(value);
    choices.push_back(std::move(choice));
  }
  item_type->set_choices(std::move(choices));
  return item_type;
}

// Builds the ArgumentSpec for a events.Rule type, given a list of actions and
// conditions. It's insufficient to use the specification in events.Rule, since
// that provides argument types of "any" for actions and conditions, allowing
// individual APIs to specify them further. Alternatively, we could lookup the
// events.Rule spec and only override the actions and conditions properties,
// but that doesn't seem any less contrived and requires JSON parsing and
// complex spec initialization.
// TODO(devlin): Another target for generating these specs. Currently, the
// custom JS bindings do something similar, so this is no worse off, but that
// doesn't make it more desirable.
std::unique_ptr<ArgumentSpec> BuildRulesSpec(
    const std::vector<std::string>& actions_list,
    const std::vector<std::string>& conditions_list) {
  auto rule_spec = std::make_unique<ArgumentSpec>(ArgumentType::OBJECT);
  ArgumentSpec::PropertiesMap properties;
  {
    auto id_spec = std::make_unique<ArgumentSpec>(ArgumentType::STRING);
    id_spec->set_optional(true);
    properties["id"] = std::move(id_spec);
  }
  {
    auto tags_spec = std::make_unique<ArgumentSpec>(ArgumentType::LIST);
    tags_spec->set_list_element_type(
        std::make_unique<ArgumentSpec>(ArgumentType::STRING));
    tags_spec->set_optional(true);
    properties["tags"] = std::move(tags_spec);
  }
  {
    auto actions_spec = std::make_unique<ArgumentSpec>(ArgumentType::LIST);
    actions_spec->set_list_element_type(BuildChoicesSpec(actions_list));
    properties["actions"] = std::move(actions_spec);
  }
  {
    auto conditions_spec = std::make_unique<ArgumentSpec>(ArgumentType::LIST);
    conditions_spec->set_list_element_type(BuildChoicesSpec(conditions_list));
    properties["conditions"] = std::move(conditions_spec);
  }
  {
    auto priority_spec = std::make_unique<ArgumentSpec>(ArgumentType::INTEGER);
    priority_spec->set_optional(true);
    properties["priority"] = std::move(priority_spec);
  }
  rule_spec->set_properties(std::move(properties));
  return rule_spec;
}

// Builds the signature for events.addRules using a specific rule.
std::unique_ptr<APISignature> BuildAddRulesSignature(
    const std::string& rule_name) {
  std::vector<std::unique_ptr<ArgumentSpec>> params;
  params.push_back(std::make_unique<ArgumentSpec>(ArgumentType::STRING));
  params.push_back(std::make_unique<ArgumentSpec>(ArgumentType::INTEGER));
  {
    auto rules = std::make_unique<ArgumentSpec>(ArgumentType::LIST);
    auto ref = std::make_unique<ArgumentSpec>(ArgumentType::REF);
    ref->set_ref(rule_name);
    rules->set_list_element_type(std::move(ref));
    params.push_back(std::move(rules));
  }
  auto returns_async = std::make_unique<APISignature::ReturnsAsync>();
  returns_async->optional = true;

  return std::make_unique<APISignature>(
      std::move(params), std::move(returns_async), nullptr /*access_checker*/);
}

}  // namespace

gin::WrapperInfo DeclarativeEvent::kWrapperInfo = {gin::kEmbedderNativeGin};

DeclarativeEvent::DeclarativeEvent(
    const std::string& name,
    APITypeReferenceMap* type_refs,
    APIRequestHandler* request_handler,
    const std::vector<std::string>& actions_list,
    const std::vector<std::string>& conditions_list,
    int webview_instance_id)
    : event_name_(name),
      type_refs_(type_refs),
      request_handler_(request_handler),
      webview_instance_id_(webview_instance_id) {
  // In declarative events, the specification of the rules can change. This only
  // matters for the events.addRules function. Check whether or not a
  // specialized version for this event exists, and, if not, create it.
  std::string add_rules_name = name + ".addRules";
  if (!type_refs->HasTypeMethodSignature(add_rules_name)) {
    // Create the specific rules spec and cache it under this type. This will
    // result in e.g. declarativeContent.onPageChanged.Rule, since the Rule
    // schema is only used for this event.
    std::unique_ptr<ArgumentSpec> rules_spec =
        BuildRulesSpec(actions_list, conditions_list);
    std::string rule_type_name = name + ".Rule";
    type_refs->AddSpec(rule_type_name, std::move(rules_spec));
    // Build a custom signature for the method, since this would be different
    // than adding rules for a different event.
    std::unique_ptr<APISignature> rules_signature =
        BuildAddRulesSignature(rule_type_name);
    type_refs->AddTypeMethodSignature(add_rules_name,
                                      std::move(rules_signature));
  }
}

DeclarativeEvent::~DeclarativeEvent() = default;

gin::ObjectTemplateBuilder DeclarativeEvent::GetObjectTemplateBuilder(
    v8::Isolate* isolate) {
  return Wrappable<DeclarativeEvent>::GetObjectTemplateBuilder(isolate)
      .SetMethod("addRules", &DeclarativeEvent::AddRules)
      .SetMethod("removeRules", &DeclarativeEvent::RemoveRules)
      .SetMethod("getRules", &DeclarativeEvent::GetRules);
}

const char* DeclarativeEvent::GetTypeName() {
  // NOTE(devlin): Currently, our documentation does not differentiate between
  // "normal" events and declarative events. Use "Event" here so that developers
  // don't think there's separate documentation to look for.
  return "Event";
}

void DeclarativeEvent::AddRules(gin::Arguments* arguments) {
  // When adding rules, we use the signature we built for this event (e.g.
  // declarativeContent.onPageChanged.addRules).
  HandleFunction(event_name_ + ".addRules", "events.addRules", arguments);
}

void DeclarativeEvent::RemoveRules(gin::Arguments* arguments) {
  // The signatures for removeRules are always the same (they don't use the
  // event's Rule schema).
  HandleFunction("events.Event.removeRules", "events.removeRules", arguments);
}

void DeclarativeEvent::GetRules(gin::Arguments* arguments) {
  // The signatures for getRules are always the same (they don't use the
  // event's Rule schema).
  HandleFunction("events.Event.getRules", "events.getRules", arguments);
}

void DeclarativeEvent::HandleFunction(const std::string& signature_name,
                                      const std::string& request_name,
                                      gin::Arguments* arguments) {
  v8::Isolate* isolate = arguments->isolate();
  v8::HandleScope handle_scope(isolate);
  v8::Local<v8::Context> context = arguments->GetHolderCreationContext();

  v8::LocalVector<v8::Value> argument_list = arguments->GetAll();

  // The events API has two undocumented parameters for each function: the name
  // of the event, and the "webViewInstanceId". Currently, stub 0 for webview
  // instance id.
  argument_list.insert(argument_list.begin(),
                       {gin::StringToSymbol(isolate, event_name_),
                        v8::Integer::New(isolate, webview_instance_id_)});

  const APISignature* signature =
      type_refs_->GetTypeMethodSignature(signature_name);
  DCHECK(signature);
  APISignature::JSONParseResult parse_result =
      signature->ParseArgumentsToJSON(context, argument_list, *type_refs_);
  if (!parse_result.succeeded()) {
    arguments->ThrowTypeError("Invalid invocation");
    return;
  }

  // We don't currently support promise based requests through DeclarativeEvent.
  DCHECK_NE(binding::AsyncResponseType::kPromise, parse_result.async_type);

  request_handler_->StartRequest(
      context, request_name, std::move(*parse_result.arguments_list),
      parse_result.async_type, parse_result.callback, v8::Local<v8::Function>(),
      binding::ResultModifierFunction());
}

}  // namespace extensions