| OLD | NEW |
| (Empty) |
| 1 // Copyright 2017 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 "extensions/renderer/declarative_event.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <memory> | |
| 9 | |
| 10 #include "base/memory/ptr_util.h" | |
| 11 #include "base/strings/stringprintf.h" | |
| 12 #include "base/values.h" | |
| 13 #include "extensions/renderer/api_event_listeners.h" | |
| 14 #include "extensions/renderer/api_request_handler.h" | |
| 15 #include "extensions/renderer/api_signature.h" | |
| 16 #include "extensions/renderer/api_type_reference_map.h" | |
| 17 #include "extensions/renderer/argument_spec.h" | |
| 18 #include "gin/object_template_builder.h" | |
| 19 #include "gin/per_context_data.h" | |
| 20 | |
| 21 namespace extensions { | |
| 22 | |
| 23 namespace { | |
| 24 | |
| 25 // Builds an ArgumentSpec that accepts the given |choices| as references. | |
| 26 std::unique_ptr<ArgumentSpec> BuildChoicesSpec( | |
| 27 const std::vector<std::string>& choices_list) { | |
| 28 auto item_type = base::MakeUnique<ArgumentSpec>(ArgumentType::CHOICES); | |
| 29 std::vector<std::unique_ptr<ArgumentSpec>> choices; | |
| 30 choices.reserve(choices_list.size()); | |
| 31 for (const std::string& value : choices_list) { | |
| 32 auto choice = base::MakeUnique<ArgumentSpec>(ArgumentType::REF); | |
| 33 choice->set_ref(value); | |
| 34 choices.push_back(std::move(choice)); | |
| 35 } | |
| 36 item_type->set_choices(std::move(choices)); | |
| 37 return item_type; | |
| 38 } | |
| 39 | |
| 40 // Builds the ArgumentSpec for a events.Rule type, given a list of actions and | |
| 41 // conditions. It's insufficient to use the specification in events.Rule, since | |
| 42 // that provides argument types of "any" for actions and conditions, allowing | |
| 43 // individual APIs to specify them further. Alternatively, we could lookup the | |
| 44 // events.Rule spec and only override the actions and conditions properties, | |
| 45 // but that doesn't seem any less contrived and requires JSON parsing and | |
| 46 // complex spec initialization. | |
| 47 // TODO(devlin): Another target for generating these specs. Currently, the | |
| 48 // custom JS bindings do something similar, so this is no worse off, but that | |
| 49 // doesn't make it more desirable. | |
| 50 std::unique_ptr<ArgumentSpec> BuildRulesSpec( | |
| 51 const std::vector<std::string>& actions_list, | |
| 52 const std::vector<std::string>& conditions_list) { | |
| 53 auto rule_spec = base::MakeUnique<ArgumentSpec>(ArgumentType::OBJECT); | |
| 54 ArgumentSpec::PropertiesMap properties; | |
| 55 { | |
| 56 auto id_spec = base::MakeUnique<ArgumentSpec>(ArgumentType::STRING); | |
| 57 id_spec->set_optional(true); | |
| 58 properties["id"] = std::move(id_spec); | |
| 59 } | |
| 60 { | |
| 61 auto tags_spec = base::MakeUnique<ArgumentSpec>(ArgumentType::LIST); | |
| 62 tags_spec->set_list_element_type( | |
| 63 base::MakeUnique<ArgumentSpec>(ArgumentType::STRING)); | |
| 64 tags_spec->set_optional(true); | |
| 65 properties["tags"] = std::move(tags_spec); | |
| 66 } | |
| 67 { | |
| 68 auto actions_spec = base::MakeUnique<ArgumentSpec>(ArgumentType::LIST); | |
| 69 actions_spec->set_list_element_type(BuildChoicesSpec(actions_list)); | |
| 70 properties["actions"] = std::move(actions_spec); | |
| 71 } | |
| 72 { | |
| 73 auto conditions_spec = base::MakeUnique<ArgumentSpec>(ArgumentType::LIST); | |
| 74 conditions_spec->set_list_element_type(BuildChoicesSpec(conditions_list)); | |
| 75 properties["conditions"] = std::move(conditions_spec); | |
| 76 } | |
| 77 { | |
| 78 auto priority_spec = base::MakeUnique<ArgumentSpec>(ArgumentType::INTEGER); | |
| 79 priority_spec->set_optional(true); | |
| 80 properties["priority"] = std::move(priority_spec); | |
| 81 } | |
| 82 rule_spec->set_properties(std::move(properties)); | |
| 83 return rule_spec; | |
| 84 } | |
| 85 | |
| 86 // Builds the signature for events.addRules using a specific rule. | |
| 87 std::unique_ptr<APISignature> BuildAddRulesSignature( | |
| 88 const std::string& rule_name) { | |
| 89 std::vector<std::unique_ptr<ArgumentSpec>> params; | |
| 90 params.push_back(base::MakeUnique<ArgumentSpec>(ArgumentType::STRING)); | |
| 91 params.push_back(base::MakeUnique<ArgumentSpec>(ArgumentType::INTEGER)); | |
| 92 { | |
| 93 auto rules = base::MakeUnique<ArgumentSpec>(ArgumentType::LIST); | |
| 94 auto ref = base::MakeUnique<ArgumentSpec>(ArgumentType::REF); | |
| 95 ref->set_ref(rule_name); | |
| 96 rules->set_list_element_type(std::move(ref)); | |
| 97 params.push_back(std::move(rules)); | |
| 98 } | |
| 99 { | |
| 100 auto callback = base::MakeUnique<ArgumentSpec>(ArgumentType::FUNCTION); | |
| 101 callback->set_optional(true); | |
| 102 params.push_back(std::move(callback)); | |
| 103 } | |
| 104 | |
| 105 return base::MakeUnique<APISignature>(std::move(params)); | |
| 106 } | |
| 107 | |
| 108 } // namespace | |
| 109 | |
| 110 gin::WrapperInfo DeclarativeEvent::kWrapperInfo = {gin::kEmbedderNativeGin}; | |
| 111 | |
| 112 DeclarativeEvent::DeclarativeEvent( | |
| 113 const std::string& name, | |
| 114 APITypeReferenceMap* type_refs, | |
| 115 APIRequestHandler* request_handler, | |
| 116 const std::vector<std::string>& actions_list, | |
| 117 const std::vector<std::string>& conditions_list, | |
| 118 int webview_instance_id) | |
| 119 : event_name_(name), | |
| 120 type_refs_(type_refs), | |
| 121 request_handler_(request_handler), | |
| 122 webview_instance_id_(webview_instance_id) { | |
| 123 // In declarative events, the specification of the rules can change. This only | |
| 124 // matters for the events.addRules function. Check whether or not a | |
| 125 // specialized version for this event exists, and, if not, create it. | |
| 126 std::string add_rules_name = name + ".addRules"; | |
| 127 if (!type_refs->HasTypeMethodSignature(add_rules_name)) { | |
| 128 // Create the specific rules spec and cache it under this type. This will | |
| 129 // result in e.g. declarativeContent.onPageChanged.Rule, since the Rule | |
| 130 // schema is only used for this event. | |
| 131 std::unique_ptr<ArgumentSpec> rules_spec = | |
| 132 BuildRulesSpec(actions_list, conditions_list); | |
| 133 std::string rule_type_name = name + ".Rule"; | |
| 134 type_refs->AddSpec(rule_type_name, std::move(rules_spec)); | |
| 135 // Build a custom signature for the method, since this would be different | |
| 136 // than adding rules for a different event. | |
| 137 std::unique_ptr<APISignature> rules_signature = | |
| 138 BuildAddRulesSignature(rule_type_name); | |
| 139 type_refs->AddTypeMethodSignature(add_rules_name, | |
| 140 std::move(rules_signature)); | |
| 141 } | |
| 142 } | |
| 143 | |
| 144 DeclarativeEvent::~DeclarativeEvent() {} | |
| 145 | |
| 146 gin::ObjectTemplateBuilder DeclarativeEvent::GetObjectTemplateBuilder( | |
| 147 v8::Isolate* isolate) { | |
| 148 return Wrappable<DeclarativeEvent>::GetObjectTemplateBuilder(isolate) | |
| 149 .SetMethod("addRules", &DeclarativeEvent::AddRules) | |
| 150 .SetMethod("removeRules", &DeclarativeEvent::RemoveRules) | |
| 151 .SetMethod("getRules", &DeclarativeEvent::GetRules); | |
| 152 } | |
| 153 | |
| 154 void DeclarativeEvent::AddRules(gin::Arguments* arguments) { | |
| 155 // When adding rules, we use the signature we built for this event (e.g. | |
| 156 // declarativeContent.onPageChanged.addRules). | |
| 157 HandleFunction(event_name_ + ".addRules", "events.addRules", arguments); | |
| 158 } | |
| 159 | |
| 160 void DeclarativeEvent::RemoveRules(gin::Arguments* arguments) { | |
| 161 // The signatures for removeRules are always the same (they don't use the | |
| 162 // event's Rule schema). | |
| 163 HandleFunction("events.Event.removeRules", "events.removeRules", arguments); | |
| 164 } | |
| 165 | |
| 166 void DeclarativeEvent::GetRules(gin::Arguments* arguments) { | |
| 167 // The signatures for getRules are always the same (they don't use the | |
| 168 // event's Rule schema). | |
| 169 HandleFunction("events.Event.getRules", "events.getRules", arguments); | |
| 170 } | |
| 171 | |
| 172 void DeclarativeEvent::HandleFunction(const std::string& signature_name, | |
| 173 const std::string& request_name, | |
| 174 gin::Arguments* arguments) { | |
| 175 v8::Isolate* isolate = arguments->isolate(); | |
| 176 v8::HandleScope handle_scope(isolate); | |
| 177 v8::Local<v8::Context> context = arguments->GetHolderCreationContext(); | |
| 178 | |
| 179 std::vector<v8::Local<v8::Value>> argument_list = arguments->GetAll(); | |
| 180 | |
| 181 // The events API has two undocumented parameters for each function: the name | |
| 182 // of the event, and the "webViewInstanceId". Currently, stub 0 for webview | |
| 183 // instance id. | |
| 184 argument_list.insert(argument_list.begin(), | |
| 185 {gin::StringToSymbol(isolate, event_name_), | |
| 186 v8::Integer::New(isolate, webview_instance_id_)}); | |
| 187 | |
| 188 std::unique_ptr<base::ListValue> converted_arguments; | |
| 189 v8::Local<v8::Function> callback; | |
| 190 std::string error; | |
| 191 const APISignature* signature = | |
| 192 type_refs_->GetTypeMethodSignature(signature_name); | |
| 193 DCHECK(signature); | |
| 194 if (!signature->ParseArgumentsToJSON(context, argument_list, *type_refs_, | |
| 195 &converted_arguments, &callback, | |
| 196 &error)) { | |
| 197 arguments->ThrowTypeError("Invalid invocation"); | |
| 198 return; | |
| 199 } | |
| 200 | |
| 201 request_handler_->StartRequest( | |
| 202 context, request_name, std::move(converted_arguments), callback, | |
| 203 v8::Local<v8::Function>(), binding::RequestThread::UI); | |
| 204 } | |
| 205 | |
| 206 } // namespace extensions | |
| OLD | NEW |