native_extension_bindings_system.cc 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004
  1. // Copyright 2016 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. #include "extensions/renderer/native_extension_bindings_system.h"
  5. #include <utility>
  6. #include "base/bind.h"
  7. #include "base/callback.h"
  8. #include "base/command_line.h"
  9. #include "base/metrics/histogram_macros.h"
  10. #include "base/strings/string_piece.h"
  11. #include "base/strings/stringprintf.h"
  12. #include "base/timer/elapsed_timer.h"
  13. #include "components/crx_file/id_util.h"
  14. #include "content/public/common/content_switches.h"
  15. #include "extensions/common/constants.h"
  16. #include "extensions/common/extension_api.h"
  17. #include "extensions/common/extension_messages.h"
  18. #include "extensions/common/features/feature.h"
  19. #include "extensions/common/features/feature_provider.h"
  20. #include "extensions/common/manifest_constants.h"
  21. #include "extensions/common/manifest_handlers/content_capabilities_handler.h"
  22. #include "extensions/common/manifest_handlers/externally_connectable.h"
  23. #include "extensions/common/mojom/event_dispatcher.mojom.h"
  24. #include "extensions/common/mojom/frame.mojom.h"
  25. #include "extensions/renderer/api_activity_logger.h"
  26. #include "extensions/renderer/bindings/api_binding_bridge.h"
  27. #include "extensions/renderer/bindings/api_binding_hooks.h"
  28. #include "extensions/renderer/bindings/api_binding_js_util.h"
  29. #include "extensions/renderer/bindings/api_binding_util.h"
  30. #include "extensions/renderer/chrome_setting.h"
  31. #include "extensions/renderer/console.h"
  32. #include "extensions/renderer/content_setting.h"
  33. #include "extensions/renderer/declarative_content_hooks_delegate.h"
  34. #include "extensions/renderer/dom_hooks_delegate.h"
  35. #include "extensions/renderer/extension_frame_helper.h"
  36. #include "extensions/renderer/extension_interaction_provider.h"
  37. #include "extensions/renderer/extension_js_runner.h"
  38. #include "extensions/renderer/get_script_context.h"
  39. #include "extensions/renderer/i18n_hooks_delegate.h"
  40. #include "extensions/renderer/ipc_message_sender.h"
  41. #include "extensions/renderer/module_system.h"
  42. #include "extensions/renderer/renderer_extension_registry.h"
  43. #include "extensions/renderer/runtime_hooks_delegate.h"
  44. #include "extensions/renderer/script_context.h"
  45. #include "extensions/renderer/script_context_set_iterable.h"
  46. #include "extensions/renderer/storage_area.h"
  47. #include "extensions/renderer/web_request_hooks.h"
  48. #include "extensions/renderer/worker_thread_util.h"
  49. #include "gin/converter.h"
  50. #include "gin/data_object_builder.h"
  51. #include "gin/handle.h"
  52. #include "gin/per_context_data.h"
  53. #include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
  54. #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
  55. #include "third_party/blink/public/web/web_document.h"
  56. #include "third_party/blink/public/web/web_local_frame.h"
  57. #include "v8/include/v8-context.h"
  58. #include "v8/include/v8-isolate.h"
  59. #include "v8/include/v8-object.h"
  60. #include "v8/include/v8-primitive.h"
  61. #include "v8/include/v8-template.h"
  62. namespace extensions {
  63. namespace {
  64. const char kBindingsSystemPerContextKey[] = "extension_bindings_system";
  65. // Returns true if the given |api| is a "prefixed" api of the |root_api|; that
  66. // is, if the api begins with the root.
  67. // For example, 'app.runtime' is a prefixed api of 'app'.
  68. // This is designed to be used as a utility when iterating over a sorted map, so
  69. // assumes that |api| is lexicographically greater than |root_api|.
  70. bool IsPrefixedAPI(base::StringPiece api, base::StringPiece root_api) {
  71. DCHECK_NE(api, root_api);
  72. DCHECK_GT(api, root_api);
  73. return base::StartsWith(api, root_api, base::CompareCase::SENSITIVE) &&
  74. api[root_api.size()] == '.';
  75. }
  76. // Returns the first different level of the api specification between the given
  77. // |api_name| and |reference|. For an api_name of 'app.runtime' and a reference
  78. // of 'app', this returns 'app.runtime'. For an api_name of
  79. // 'cast.streaming.session' and a reference of 'cast', this returns
  80. // 'cast.streaming'. If reference is empty, this simply returns the first layer;
  81. // so given 'app.runtime' and no reference, this returns 'app'.
  82. base::StringPiece GetFirstDifferentAPIName(base::StringPiece api_name,
  83. base::StringPiece reference) {
  84. base::StringPiece::size_type dot =
  85. api_name.find('.', reference.empty() ? 0 : reference.size() + 1);
  86. if (dot == base::StringPiece::npos)
  87. return api_name;
  88. return api_name.substr(0, dot);
  89. }
  90. struct BindingsSystemPerContextData : public base::SupportsUserData::Data {
  91. BindingsSystemPerContextData(
  92. base::WeakPtr<NativeExtensionBindingsSystem> bindings_system)
  93. : bindings_system(bindings_system) {}
  94. ~BindingsSystemPerContextData() override {}
  95. v8::Global<v8::Object> api_object;
  96. v8::Global<v8::Object> internal_apis;
  97. base::WeakPtr<NativeExtensionBindingsSystem> bindings_system;
  98. };
  99. // If a 'chrome' property exists on the context's global and is an object,
  100. // returns that.
  101. // If a 'chrome' property exists but isn't an object, returns an empty Local.
  102. // If no 'chrome' property exists (or is undefined), creates a new
  103. // object, assigns it to Global().chrome, and returns it.
  104. v8::Local<v8::Object> GetOrCreateChrome(v8::Local<v8::Context> context) {
  105. // Ensure that the creation context for any new chrome object is |context|.
  106. v8::Context::Scope context_scope(context);
  107. // TODO(devlin): This is a little silly. We expect that this may do the wrong
  108. // thing if the window has set some other 'chrome' (as in the case of script
  109. // doing 'window.chrome = true'), but we don't really handle it. It could also
  110. // throw exceptions or have unintended side effects.
  111. // On the one hand, anyone writing that code is probably asking for trouble.
  112. // On the other, it'd be nice to avoid. I wonder if we can?
  113. v8::Local<v8::String> chrome_string =
  114. gin::StringToSymbol(context->GetIsolate(), "chrome");
  115. v8::Local<v8::Value> chrome_value;
  116. if (!context->Global()->Get(context, chrome_string).ToLocal(&chrome_value))
  117. return v8::Local<v8::Object>();
  118. v8::Local<v8::Object> chrome_object;
  119. if (chrome_value->IsUndefined()) {
  120. chrome_object = v8::Object::New(context->GetIsolate());
  121. v8::Maybe<bool> success = context->Global()->CreateDataProperty(
  122. context, chrome_string, chrome_object);
  123. if (!success.IsJust() || !success.FromJust())
  124. return v8::Local<v8::Object>();
  125. } else if (chrome_value->IsObject()) {
  126. v8::Local<v8::Object> obj = chrome_value.As<v8::Object>();
  127. // The creation context of the `chrome` property could be different if a
  128. // different context (such as the parent of an about:blank iframe) assigned
  129. // it. Since in this case we know that the chrome object is not the one we
  130. // created, do not use it for bindings. This also avoids weirdness of having
  131. // bindings created in one context stored on a chrome object from another.
  132. // TODO(devlin): There might be a way of detecting if the browser created
  133. // the chrome object. For instance, we could add a v8::Private to the
  134. // chrome object we construct, and check if it's present. Unfortunately, we
  135. // need to a) track down each place we create the chrome object (it's not
  136. // just in extensions) and also see how much that would break.
  137. if (obj->GetCreationContextChecked() == context)
  138. chrome_object = obj;
  139. }
  140. return chrome_object;
  141. }
  142. BindingsSystemPerContextData* GetBindingsDataFromContext(
  143. v8::Local<v8::Context> context) {
  144. gin::PerContextData* per_context_data = gin::PerContextData::From(context);
  145. if (!per_context_data || !binding::IsContextValid(context))
  146. return nullptr; // Context is shutting down.
  147. auto* data = static_cast<BindingsSystemPerContextData*>(
  148. per_context_data->GetUserData(kBindingsSystemPerContextKey));
  149. CHECK(data);
  150. if (!data->bindings_system) {
  151. NOTREACHED() << "Context outlived bindings system.";
  152. return nullptr;
  153. }
  154. return data;
  155. }
  156. void AddConsoleError(v8::Local<v8::Context> context, const std::string& error) {
  157. ScriptContext* script_context = GetScriptContextFromV8Context(context);
  158. // Note: |script_context| may be null. During context tear down, we remove the
  159. // script context from the ScriptContextSet, so it's not findable by
  160. // GetScriptContextFromV8Context. In theory, we shouldn't be running any
  161. // bindings code after this point, but it seems that we are in at least some
  162. // places.
  163. // TODO(devlin): Investigate. At least one place this manifests is in
  164. // messaging binding tear down exhibited by
  165. // MessagingApiTest.MessagingBackgroundOnly.
  166. // console::AddMessage() can handle a null script context.
  167. console::AddMessage(script_context, blink::mojom::ConsoleMessageLevel::kError,
  168. error);
  169. }
  170. // Returns the API schema indicated by |api_name|.
  171. const base::DictionaryValue& GetAPISchema(const std::string& api_name) {
  172. const base::DictionaryValue* schema =
  173. ExtensionAPI::GetSharedInstance()->GetSchema(api_name);
  174. CHECK(schema) << api_name;
  175. return *schema;
  176. }
  177. // Returns true if the feature specified by |name| is available to the given
  178. // |context|.
  179. bool IsAPIFeatureAvailable(v8::Local<v8::Context> context,
  180. const std::string& name) {
  181. ScriptContext* script_context = GetScriptContextFromV8ContextChecked(context);
  182. return script_context->GetAvailability(name).is_available();
  183. }
  184. // Returns true if the specified |context| is allowed to use promise based
  185. // returns from APIs.
  186. bool ArePromisesAllowed(v8::Local<v8::Context> context) {
  187. ScriptContext* script_context = GetScriptContextFromV8ContextChecked(context);
  188. const Extension* extension = script_context->extension();
  189. return (extension && extension->manifest_version() >= 3) ||
  190. script_context->context_type() == Feature::WEBUI_CONTEXT;
  191. }
  192. // Instantiates the binding object for the given |name|. |name| must specify a
  193. // specific feature.
  194. v8::Local<v8::Object> CreateRootBinding(v8::Local<v8::Context> context,
  195. ScriptContext* script_context,
  196. const std::string& name,
  197. APIBindingsSystem* bindings_system) {
  198. APIBindingHooks* hooks = nullptr;
  199. v8::Local<v8::Object> binding_object =
  200. bindings_system->CreateAPIInstance(name, context, &hooks);
  201. gin::Handle<APIBindingBridge> bridge_handle = gin::CreateHandle(
  202. context->GetIsolate(),
  203. new APIBindingBridge(hooks, context, binding_object,
  204. script_context->GetExtensionID(),
  205. script_context->GetContextTypeDescription()));
  206. v8::Local<v8::Value> native_api_bridge = bridge_handle.ToV8();
  207. script_context->module_system()->OnNativeBindingCreated(name,
  208. native_api_bridge);
  209. return binding_object;
  210. }
  211. // Creates the binding object for the given |root_name|. This can be
  212. // complicated, since APIs may have prefixed names, like 'app.runtime' or
  213. // 'system.cpu'. This method accepts the first name (i.e., the key that we are
  214. // looking for on the chrome object, such as 'app') and returns the fully
  215. // instantiated binding, including prefixed APIs. That is, given 'app', this
  216. // will instantiate 'app', 'app.runtime', and 'app.window'.
  217. //
  218. // NOTE(devlin): We could do the prefixed apis lazily; however, it's not clear
  219. // how much of a win it would be. It's less overhead here than in the general
  220. // case (instantiating a handful of APIs instead of all of them), and it's more
  221. // likely they will be used (since the extension is already accessing the
  222. // parent).
  223. // TODO(devlin): We should be creating ObjectTemplates for these so that we only
  224. // do this work once. APIBindings (for the single API) already do this.
  225. v8::Local<v8::Object> CreateFullBinding(
  226. v8::Local<v8::Context> context,
  227. ScriptContext* script_context,
  228. APIBindingsSystem* bindings_system,
  229. const FeatureProvider* api_feature_provider,
  230. const std::string& root_name) {
  231. const FeatureMap& features = api_feature_provider->GetAllFeatures();
  232. auto lower = features.lower_bound(root_name);
  233. DCHECK(lower != features.end());
  234. // Some bindings have a prefixed name, like app.runtime, where 'app' and
  235. // 'app.runtime' are, in fact, separate APIs. It's also possible for a
  236. // context to have access to 'app.runtime', but not to 'app'. For this, we
  237. // either instantiate the 'app' binding fully (if the context has access), or
  238. // else use an empty object (so we can still instantiate 'app.runtime').
  239. v8::Local<v8::Object> root_binding;
  240. if (lower->first == root_name) {
  241. const Feature* feature = lower->second.get();
  242. if (script_context->IsAnyFeatureAvailableToContext(
  243. *feature, CheckAliasStatus::NOT_ALLOWED)) {
  244. // If this feature is an alias for a different API, use the other binding
  245. // as the basis for the API contents.
  246. const std::string& source_name =
  247. feature->source().empty() ? root_name : feature->source();
  248. root_binding = CreateRootBinding(context, script_context, source_name,
  249. bindings_system);
  250. }
  251. ++lower;
  252. }
  253. // Look for any bindings that would be on the same object. Any of these would
  254. // start with the same base name (e.g. 'app') + '.' (since '.' is < x for any
  255. // isalpha(x)).
  256. std::string upper = root_name + static_cast<char>('.' + 1);
  257. base::StringPiece last_binding_name;
  258. // The following loop is a little painful because we have crazy binding names
  259. // and syntaxes. The way this works is as follows:
  260. // Look at each feature after the root feature we passed in. If there exists
  261. // a (non-child) feature with a prefixed name, create the full binding for
  262. // the object that the next feature is on. Then, iterate past any features
  263. // already instantiated by that, and continue until there are no more features
  264. // prefixed by the root API.
  265. // As a concrete example, we can look at the cast APIs (cast and
  266. // cast.streaming.*)
  267. // Start with vanilla 'cast', and instantiate that.
  268. // Then iterate over features, and see 'cast.streaming.receiverSession'.
  269. // 'cast.streaming.receiverSession' is a prefixed API of 'cast', but we find
  270. // the first level of difference, which is 'cast.streaming', and instantiate
  271. // that object completely (through recursion).
  272. // The next feature is 'cast.streaming.rtpStream', but this is a prefixed API
  273. // of 'cast.streaming', which we just instantiated completely (including
  274. // 'cast.streaming.rtpStream'), so we continue.
  275. // Iterate until all cast.* features are created.
  276. // TODO(devlin): This is bonkers, but what's the better way? We could extract
  277. // this out to be a more readable Visitor implementation, but is it worth it
  278. // for this one place? Ideally, we'd have a less convoluted feature
  279. // representation (some kind of tree would make this trivial), but for now, we
  280. // have strings.
  281. // On the upside, most APIs are not prefixed at all, and this loop is never
  282. // entered.
  283. for (auto iter = lower; iter != features.end() && iter->first < upper;
  284. ++iter) {
  285. if (iter->second->IsInternal())
  286. continue;
  287. if (IsPrefixedAPI(iter->first, last_binding_name)) {
  288. // Instantiating |last_binding_name| must have already instantiated
  289. // iter->first.
  290. continue;
  291. }
  292. // If this API has a parent feature (and isn't marked 'noparent'),
  293. // then this must be a function or event, so we should not register.
  294. if (api_feature_provider->GetParent(*iter->second) != nullptr)
  295. continue;
  296. base::StringPiece binding_name =
  297. GetFirstDifferentAPIName(iter->first, root_name);
  298. v8::Local<v8::Object> nested_binding =
  299. CreateFullBinding(context, script_context, bindings_system,
  300. api_feature_provider, std::string(binding_name));
  301. // It's possible that we don't create a binding if no features or
  302. // prefixed features are available to the context.
  303. if (nested_binding.IsEmpty())
  304. continue;
  305. if (root_binding.IsEmpty())
  306. root_binding = v8::Object::New(context->GetIsolate());
  307. // The nested api name contains a '.', e.g. 'app.runtime', but we want to
  308. // expose it on the object simply as 'runtime'.
  309. // Cache the last_binding_name now before mangling it.
  310. last_binding_name = binding_name;
  311. DCHECK_NE(base::StringPiece::npos, binding_name.rfind('.'));
  312. base::StringPiece accessor_name =
  313. binding_name.substr(binding_name.rfind('.') + 1);
  314. v8::Local<v8::String> nested_name =
  315. gin::StringToSymbol(context->GetIsolate(), accessor_name);
  316. v8::Maybe<bool> success =
  317. root_binding->CreateDataProperty(context, nested_name, nested_binding);
  318. if (!success.IsJust() || !success.FromJust())
  319. return v8::Local<v8::Object>();
  320. }
  321. return root_binding;
  322. }
  323. std::string GetContextOwner(v8::Local<v8::Context> context) {
  324. ScriptContext* script_context = GetScriptContextFromV8ContextChecked(context);
  325. const std::string& extension_id = script_context->GetExtensionID();
  326. bool id_is_valid = crx_file::id_util::IdIsValid(extension_id);
  327. CHECK(id_is_valid || script_context->url().is_valid());
  328. // Use only origin for URLs to match browser logic in EventListener::ForURL().
  329. return id_is_valid
  330. ? extension_id
  331. : url::Origin::Create(script_context->url()).GetURL().spec();
  332. }
  333. // Returns true if any portion of the runtime API is available to the given
  334. // |context|. This is different than just checking features because runtime's
  335. // availability depends on the installed extensions and the active URL (in the
  336. // case of extensions communicating with external websites).
  337. bool IsRuntimeAvailableToContext(ScriptContext* context) {
  338. // TODO(devlin): This doesn't seem thread-safe with ServiceWorkers?
  339. for (const auto& extension :
  340. *RendererExtensionRegistry::Get()->GetMainThreadExtensionSet()) {
  341. ExternallyConnectableInfo* info = static_cast<ExternallyConnectableInfo*>(
  342. extension->GetManifestData(manifest_keys::kExternallyConnectable));
  343. if (info && info->matches.MatchesURL(context->url())) {
  344. return true;
  345. }
  346. }
  347. return false;
  348. }
  349. // The APIs that could potentially be available to webpage-like contexts.
  350. // This is the list of possible features; most web pages will not have access
  351. // to these APIs.
  352. // Note: `runtime` is not included here, since it's handled specially above.
  353. const char* const kWebAvailableFeatures[] = {
  354. "app",
  355. "dashboardPrivate",
  356. "webstorePrivate",
  357. "management",
  358. };
  359. } // namespace
  360. NativeExtensionBindingsSystem::NativeExtensionBindingsSystem(
  361. std::unique_ptr<IPCMessageSender> ipc_message_sender)
  362. : ipc_message_sender_(std::move(ipc_message_sender)),
  363. api_system_(
  364. base::BindRepeating(&GetAPISchema),
  365. base::BindRepeating(&IsAPIFeatureAvailable),
  366. base::BindRepeating(&ArePromisesAllowed),
  367. base::BindRepeating(&NativeExtensionBindingsSystem::SendRequest,
  368. base::Unretained(this)),
  369. std::make_unique<ExtensionInteractionProvider>(),
  370. base::BindRepeating(
  371. &NativeExtensionBindingsSystem::OnEventListenerChanged,
  372. base::Unretained(this)),
  373. base::BindRepeating(&GetContextOwner),
  374. base::BindRepeating(&APIActivityLogger::LogAPICall,
  375. ipc_message_sender_.get()),
  376. base::BindRepeating(&AddConsoleError),
  377. APILastError(base::BindRepeating(&GetLastErrorParents),
  378. base::BindRepeating(&AddConsoleError))),
  379. messaging_service_(this) {
  380. api_system_.RegisterCustomType(
  381. "storage.StorageArea",
  382. base::BindRepeating(&StorageArea::CreateStorageArea));
  383. api_system_.RegisterCustomType("types.ChromeSetting",
  384. base::BindRepeating(&ChromeSetting::Create));
  385. api_system_.RegisterCustomType("contentSettings.ContentSetting",
  386. base::BindRepeating(&ContentSetting::Create));
  387. api_system_.GetHooksForAPI("webRequest")
  388. ->SetDelegate(std::make_unique<WebRequestHooks>());
  389. api_system_.GetHooksForAPI("declarativeContent")
  390. ->SetDelegate(std::make_unique<DeclarativeContentHooksDelegate>());
  391. api_system_.GetHooksForAPI("dom")->SetDelegate(
  392. std::make_unique<DOMHooksDelegate>());
  393. api_system_.GetHooksForAPI("i18n")->SetDelegate(
  394. std::make_unique<I18nHooksDelegate>());
  395. api_system_.GetHooksForAPI("runtime")->SetDelegate(
  396. std::make_unique<RuntimeHooksDelegate>(&messaging_service_));
  397. }
  398. NativeExtensionBindingsSystem::~NativeExtensionBindingsSystem() {}
  399. void NativeExtensionBindingsSystem::DidCreateScriptContext(
  400. ScriptContext* context) {
  401. v8::Isolate* isolate = context->isolate();
  402. v8::HandleScope handle_scope(isolate);
  403. v8::Local<v8::Context> v8_context = context->v8_context();
  404. JSRunner::SetInstanceForContext(v8_context,
  405. std::make_unique<ExtensionJSRunner>(context));
  406. gin::PerContextData* per_context_data = gin::PerContextData::From(v8_context);
  407. DCHECK(per_context_data);
  408. DCHECK(!per_context_data->GetUserData(kBindingsSystemPerContextKey));
  409. auto data = std::make_unique<BindingsSystemPerContextData>(
  410. weak_factory_.GetWeakPtr());
  411. per_context_data->SetUserData(kBindingsSystemPerContextKey, std::move(data));
  412. if (get_internal_api_.IsEmpty()) {
  413. get_internal_api_.Set(
  414. isolate, v8::FunctionTemplate::New(
  415. isolate, &NativeExtensionBindingsSystem::GetInternalAPI,
  416. v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0,
  417. v8::ConstructorBehavior::kThrow));
  418. }
  419. // Note: it's a shame we can't delay this (until, say, we knew an API would
  420. // actually be used), but it's needed for some of our crazier hooks, like
  421. // web/guest view.
  422. context->module_system()->SetGetInternalAPIHook(
  423. get_internal_api_.Get(isolate));
  424. context->module_system()->SetJSBindingUtilGetter(
  425. base::BindRepeating(&NativeExtensionBindingsSystem::GetJSBindingUtil,
  426. weak_factory_.GetWeakPtr()));
  427. UpdateBindingsForContext(context);
  428. // Set the scripting params object for if we are running in a content script
  429. // context. This effectively checks that we are running in an isolated world
  430. // since main world script contexts have a different Feature::Context type.
  431. if (context->context_type() == Feature::CONTENT_SCRIPT_CONTEXT)
  432. SetScriptingParams(context);
  433. }
  434. void NativeExtensionBindingsSystem::WillReleaseScriptContext(
  435. ScriptContext* context) {
  436. v8::HandleScope handle_scope(context->isolate());
  437. v8::Local<v8::Context> v8_context = context->v8_context();
  438. api_system_.WillReleaseContext(v8_context);
  439. // Clear the JSRunner only after everything else has been notified that the
  440. // context is being released.
  441. JSRunner::ClearInstanceForContext(v8_context);
  442. }
  443. void NativeExtensionBindingsSystem::UpdateBindingsForContext(
  444. ScriptContext* context) {
  445. v8::Isolate* isolate = context->isolate();
  446. v8::HandleScope handle_scope(isolate);
  447. v8::Local<v8::Context> v8_context = context->v8_context();
  448. v8::Local<v8::Object> chrome = GetOrCreateChrome(v8_context);
  449. if (chrome.IsEmpty())
  450. return;
  451. DCHECK(GetBindingsDataFromContext(v8_context));
  452. auto set_accessor = [chrome, isolate,
  453. v8_context](base::StringPiece accessor_name) {
  454. v8::Local<v8::String> api_name =
  455. gin::StringToSymbol(isolate, accessor_name);
  456. v8::Maybe<bool> success = chrome->SetLazyDataProperty(
  457. v8_context, api_name, &BindingAccessor, api_name);
  458. return success.IsJust() && success.FromJust();
  459. };
  460. auto set_restricted_accessor = [chrome, isolate,
  461. v8_context](base::StringPiece accessor_name) {
  462. v8::Local<v8::String> api_name =
  463. gin::StringToSymbol(isolate, accessor_name);
  464. v8::Maybe<bool> success = chrome->SetLazyDataProperty(
  465. v8_context, api_name, &ThrowDeveloperModeRestrictedError, api_name);
  466. return success.IsJust() && success.FromJust();
  467. };
  468. bool is_webpage = false;
  469. switch (context->context_type()) {
  470. case Feature::UNSPECIFIED_CONTEXT:
  471. case Feature::WEB_PAGE_CONTEXT:
  472. case Feature::BLESSED_WEB_PAGE_CONTEXT:
  473. is_webpage = true;
  474. break;
  475. case Feature::BLESSED_EXTENSION_CONTEXT:
  476. case Feature::LOCK_SCREEN_EXTENSION_CONTEXT:
  477. case Feature::OFFSCREEN_EXTENSION_CONTEXT:
  478. case Feature::UNBLESSED_EXTENSION_CONTEXT:
  479. case Feature::CONTENT_SCRIPT_CONTEXT:
  480. case Feature::WEBUI_CONTEXT:
  481. case Feature::WEBUI_UNTRUSTED_CONTEXT:
  482. is_webpage = false;
  483. }
  484. if (is_webpage) {
  485. // Hard-code registration of any APIs that are exposed to webpage-like
  486. // contexts, because it's more expensive to iterate over all the existing
  487. // features when only a handful could ever be available.
  488. // All of the same permission checks will still apply.
  489. // TODO(devlin): It could be interesting to apply this same logic to all
  490. // context types, especially on a given platform. Something to think about
  491. // for when we generate features.
  492. for (const char* feature_name : kWebAvailableFeatures) {
  493. if (context->GetAvailability(feature_name).is_available() &&
  494. !set_accessor(feature_name)) {
  495. LOG(ERROR) << "Failed to create API on Chrome object.";
  496. return;
  497. }
  498. }
  499. // Runtime is special (see IsRuntimeAvailableToContext()).
  500. if (IsRuntimeAvailableToContext(context) && !set_accessor("runtime"))
  501. LOG(ERROR) << "Failed to create API on Chrome object.";
  502. UpdateContentCapabilities(context);
  503. return;
  504. }
  505. FeatureCache::FeatureNameVector features =
  506. feature_cache_.GetAvailableFeatures(context->context_type(),
  507. context->extension(), context->url());
  508. base::StringPiece last_accessor;
  509. for (const std::string& feature : features) {
  510. // If we've already set up an accessor for the immediate property of the
  511. // chrome object, we don't need to do more.
  512. if (IsPrefixedAPI(feature, last_accessor))
  513. continue;
  514. // We've found an API that's available to the extension. Normally, we will
  515. // expose this under the name of the feature (e.g., 'tabs'), but in some
  516. // cases, this will be a prefixed API, such as 'app.runtime'. Find what the
  517. // property on the chrome object is named, and use that. So in the case of
  518. // 'app.runtime', we surface a getter for simply 'app'.
  519. //
  520. // TODO(devlin): UpdateBindingsForContext can be called during context
  521. // creation, but also when e.g. permissions change. Do we need to be
  522. // checking for whether or not the API already exists on the object as well
  523. // as if we need to remove any existing APIs?
  524. base::StringPiece accessor_name =
  525. GetFirstDifferentAPIName(feature, base::StringPiece());
  526. last_accessor = accessor_name;
  527. if (!set_accessor(accessor_name)) {
  528. LOG(ERROR) << "Failed to create API on Chrome object.";
  529. return;
  530. }
  531. }
  532. FeatureCache::FeatureNameVector dev_mode_features =
  533. feature_cache_.GetDeveloperModeRestrictedFeatures(
  534. context->context_type(), context->extension(), context->url());
  535. for (const std::string& feature : dev_mode_features) {
  536. base::StringPiece accessor_name =
  537. GetFirstDifferentAPIName(feature, base::StringPiece());
  538. // This code only works for restricting top-level features to developer
  539. // mode. For sub-features, this would result in overwriting the accessor
  540. // for the root API object and restricting the whole API.
  541. DCHECK_EQ(accessor_name, feature);
  542. if (!set_restricted_accessor(accessor_name)) {
  543. LOG(ERROR) << "Failed to create API on Chrome object.";
  544. return;
  545. }
  546. }
  547. }
  548. void NativeExtensionBindingsSystem::DispatchEventInContext(
  549. const std::string& event_name,
  550. const base::Value::List& event_args,
  551. const mojom::EventFilteringInfoPtr& filtering_info,
  552. ScriptContext* context) {
  553. v8::HandleScope handle_scope(context->isolate());
  554. v8::Context::Scope context_scope(context->v8_context());
  555. api_system_.FireEventInContext(event_name, context->v8_context(), event_args,
  556. filtering_info.Clone());
  557. }
  558. bool NativeExtensionBindingsSystem::HasEventListenerInContext(
  559. const std::string& event_name,
  560. ScriptContext* context) {
  561. v8::HandleScope handle_scope(context->isolate());
  562. return api_system_.event_handler()->HasListenerForEvent(
  563. event_name, context->v8_context());
  564. }
  565. void NativeExtensionBindingsSystem::HandleResponse(
  566. int request_id,
  567. bool success,
  568. const base::Value::List& response,
  569. const std::string& error) {
  570. // Some API calls result in failure, but don't set an error. Use a generic and
  571. // unhelpful error string.
  572. // TODO(devlin): Track these down and fix them. See crbug.com/648275.
  573. api_system_.CompleteRequest(
  574. request_id, response,
  575. !success && error.empty() ? "Unknown error." : error);
  576. ipc_message_sender_->SendOnRequestResponseReceivedIPC(request_id);
  577. }
  578. IPCMessageSender* NativeExtensionBindingsSystem::GetIPCMessageSender() {
  579. return ipc_message_sender_.get();
  580. }
  581. void NativeExtensionBindingsSystem::UpdateBindings(
  582. const ExtensionId& extension_id,
  583. bool permissions_changed,
  584. ScriptContextSetIterable* script_context_set) {
  585. if (permissions_changed)
  586. InvalidateFeatureCache(extension_id);
  587. script_context_set->ForEach(
  588. extension_id,
  589. base::BindRepeating(
  590. &NativeExtensionBindingsSystem::UpdateBindingsForContext,
  591. // Called synchronously.
  592. base::Unretained(this)));
  593. }
  594. void NativeExtensionBindingsSystem::OnExtensionRemoved(const ExtensionId& id) {
  595. feature_cache_.InvalidateExtension(id);
  596. }
  597. v8::Local<v8::Object> NativeExtensionBindingsSystem::GetAPIObjectForTesting(
  598. ScriptContext* context,
  599. const std::string& api_name) {
  600. return GetAPIHelper(context->v8_context(),
  601. gin::StringToSymbol(context->isolate(), api_name));
  602. }
  603. void NativeExtensionBindingsSystem::BindingAccessor(
  604. v8::Local<v8::Name> name,
  605. const v8::PropertyCallbackInfo<v8::Value>& info) {
  606. v8::Isolate* isolate = info.GetIsolate();
  607. v8::HandleScope handle_scope(isolate);
  608. v8::Local<v8::Context> context = info.Holder()->GetCreationContextChecked();
  609. // Force binding creation in the owning context (even if another context is
  610. // calling in). This is also important to ensure that objects created through
  611. // the initialization process are all instantiated for the owning context.
  612. // See https://crbug.com/819968.
  613. v8::Context::Scope context_scope(context);
  614. // We use info.Data() to store a real name here instead of using the provided
  615. // one to handle any weirdness from the caller (non-existent strings, etc).
  616. v8::Local<v8::String> api_name = info.Data().As<v8::String>();
  617. v8::Local<v8::Object> binding = GetAPIHelper(context, api_name);
  618. if (!binding.IsEmpty())
  619. info.GetReturnValue().Set(binding);
  620. }
  621. void NativeExtensionBindingsSystem::ThrowDeveloperModeRestrictedError(
  622. v8::Local<v8::Name> name,
  623. const v8::PropertyCallbackInfo<v8::Value>& info) {
  624. v8::Isolate* isolate = info.GetIsolate();
  625. isolate->ThrowException(v8::Exception::Error(gin::StringToV8(
  626. isolate,
  627. base::StringPrintf(
  628. "The '%s' API is only available for users in developer mode.",
  629. gin::V8ToString(isolate, info.Data()).c_str()))));
  630. return;
  631. }
  632. // static
  633. v8::Local<v8::Object> NativeExtensionBindingsSystem::GetAPIHelper(
  634. v8::Local<v8::Context> context,
  635. v8::Local<v8::String> api_name) {
  636. BindingsSystemPerContextData* data = GetBindingsDataFromContext(context);
  637. if (!data)
  638. return v8::Local<v8::Object>();
  639. v8::Isolate* isolate = context->GetIsolate();
  640. v8::Local<v8::Object> apis;
  641. if (data->api_object.IsEmpty()) {
  642. apis = v8::Object::New(isolate);
  643. data->api_object = v8::Global<v8::Object>(isolate, apis);
  644. } else {
  645. apis = data->api_object.Get(isolate);
  646. }
  647. v8::Maybe<bool> has_property = apis->HasRealNamedProperty(context, api_name);
  648. if (!has_property.IsJust())
  649. return v8::Local<v8::Object>();
  650. if (has_property.FromJust()) {
  651. v8::Local<v8::Value> value =
  652. apis->GetRealNamedProperty(context, api_name).ToLocalChecked();
  653. DCHECK(value->IsObject());
  654. return value.As<v8::Object>();
  655. }
  656. ScriptContext* script_context = GetScriptContextFromV8ContextChecked(context);
  657. std::string api_name_string;
  658. CHECK(
  659. gin::Converter<std::string>::FromV8(isolate, api_name, &api_name_string));
  660. base::ElapsedTimer timer;
  661. v8::Local<v8::Object> root_binding = CreateFullBinding(
  662. context, script_context, &data->bindings_system->api_system_,
  663. FeatureProvider::GetAPIFeatures(), api_name_string);
  664. if (root_binding.IsEmpty())
  665. return v8::Local<v8::Object>();
  666. v8::Maybe<bool> success =
  667. apis->CreateDataProperty(context, api_name, root_binding);
  668. if (!success.IsJust() || !success.FromJust())
  669. return v8::Local<v8::Object>();
  670. UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions.Bindings.NativeBindingCreationTime",
  671. timer.Elapsed().InMicroseconds(), 1, 10000000,
  672. 50);
  673. return root_binding;
  674. }
  675. v8::Local<v8::Object> NativeExtensionBindingsSystem::GetLastErrorParents(
  676. v8::Local<v8::Context> context,
  677. v8::Local<v8::Object>* secondary_parent) {
  678. if (secondary_parent &&
  679. IsAPIFeatureAvailable(context, "extension.lastError")) {
  680. *secondary_parent = GetAPIHelper(
  681. context, gin::StringToSymbol(context->GetIsolate(), "extension"));
  682. }
  683. return GetAPIHelper(context,
  684. gin::StringToSymbol(context->GetIsolate(), "runtime"));
  685. }
  686. // static
  687. void NativeExtensionBindingsSystem::GetInternalAPI(
  688. const v8::FunctionCallbackInfo<v8::Value>& info) {
  689. CHECK_EQ(1, info.Length());
  690. CHECK(info[0]->IsString());
  691. v8::Isolate* isolate = info.GetIsolate();
  692. v8::HandleScope handle_scope(isolate);
  693. v8::Local<v8::Context> context = isolate->GetCurrentContext();
  694. v8::Local<v8::String> v8_name = info[0].As<v8::String>();
  695. BindingsSystemPerContextData* data = GetBindingsDataFromContext(context);
  696. CHECK(data);
  697. v8::Local<v8::Object> internal_apis;
  698. if (data->internal_apis.IsEmpty()) {
  699. internal_apis = v8::Object::New(isolate);
  700. data->internal_apis.Reset(isolate, internal_apis);
  701. } else {
  702. internal_apis = data->internal_apis.Get(isolate);
  703. }
  704. v8::Maybe<bool> has_property =
  705. internal_apis->HasOwnProperty(context, v8_name);
  706. if (!has_property.IsJust())
  707. return;
  708. if (has_property.FromJust()) {
  709. // API was already instantiated.
  710. info.GetReturnValue().Set(
  711. internal_apis->GetRealNamedProperty(context, v8_name).ToLocalChecked());
  712. return;
  713. }
  714. std::string api_name = gin::V8ToString(isolate, info[0]);
  715. const Feature* feature = FeatureProvider::GetAPIFeature(api_name);
  716. ScriptContext* script_context = GetScriptContextFromV8ContextChecked(context);
  717. if (!feature || !script_context->IsAnyFeatureAvailableToContext(
  718. *feature, CheckAliasStatus::NOT_ALLOWED)) {
  719. NOTREACHED();
  720. return;
  721. }
  722. CHECK(feature->IsInternal());
  723. // We don't need to go through CreateFullBinding here because internal APIs
  724. // are always acquired through getInternalBinding and specified by full name,
  725. // rather than through access on the chrome object. So we can just instantiate
  726. // a binding keyed with any name, even a prefixed one (e.g.
  727. // 'app.currentWindowInternal').
  728. v8::Local<v8::Object> api_binding = CreateRootBinding(
  729. context, script_context, api_name, &data->bindings_system->api_system_);
  730. if (api_binding.IsEmpty())
  731. return;
  732. v8::Maybe<bool> success =
  733. internal_apis->CreateDataProperty(context, v8_name, api_binding);
  734. if (!success.IsJust() || !success.FromJust())
  735. return;
  736. info.GetReturnValue().Set(api_binding);
  737. }
  738. void NativeExtensionBindingsSystem::SendRequest(
  739. std::unique_ptr<APIRequestHandler::Request> request,
  740. v8::Local<v8::Context> context) {
  741. ScriptContext* script_context = GetScriptContextFromV8ContextChecked(context);
  742. GURL url;
  743. blink::WebLocalFrame* frame = script_context->web_frame();
  744. if (frame && !frame->GetDocument().IsNull())
  745. url = frame->GetDocument().Url();
  746. else
  747. url = script_context->url();
  748. auto params = mojom::RequestParams::New();
  749. params->name = request->method_name;
  750. base::Value::List args = std::move(request->arguments_list->GetList());
  751. params->arguments = std::move(args);
  752. params->extension_id = script_context->GetExtensionID();
  753. params->source_url = url;
  754. params->request_id = request->request_id;
  755. params->has_callback = request->has_async_response_handler;
  756. params->user_gesture = request->has_user_gesture;
  757. // The IPC sender will update these members, if appropriate.
  758. params->worker_thread_id = kMainThreadId;
  759. params->service_worker_version_id =
  760. blink::mojom::kInvalidServiceWorkerVersionId;
  761. ipc_message_sender_->SendRequestIPC(script_context, std::move(params));
  762. }
  763. void NativeExtensionBindingsSystem::OnEventListenerChanged(
  764. const std::string& event_name,
  765. binding::EventListenersChanged change,
  766. const base::DictionaryValue* filter,
  767. bool update_lazy_listeners,
  768. v8::Local<v8::Context> context) {
  769. ScriptContext* script_context = GetScriptContextFromV8ContextChecked(context);
  770. // We only remove a lazy listener if the listener removal was triggered
  771. // manually by the extension and the context is a lazy context.
  772. // Note: Check context_type() first to avoid accessing ExtensionFrameHelper on
  773. // a worker thread.
  774. bool is_lazy = update_lazy_listeners &&
  775. (script_context->IsForServiceWorker() ||
  776. ExtensionFrameHelper::IsContextForEventPage(script_context));
  777. switch (change) {
  778. case binding::EventListenersChanged::
  779. kFirstUnfilteredListenerForContextOwnerAdded:
  780. // Send a message to add a new listener since this is the first listener
  781. // for the context owner (e.g., extension).
  782. ipc_message_sender_->SendAddUnfilteredEventListenerIPC(script_context,
  783. event_name);
  784. // Check if we need to add a lazy listener as well.
  785. [[fallthrough]];
  786. case binding::EventListenersChanged::
  787. kFirstUnfilteredListenerForContextAdded: {
  788. // If the listener is the first for the event page, we need to
  789. // specifically add a lazy listener.
  790. if (is_lazy) {
  791. ipc_message_sender_->SendAddUnfilteredLazyEventListenerIPC(
  792. script_context, event_name);
  793. }
  794. break;
  795. }
  796. case binding::EventListenersChanged::
  797. kLastUnfilteredListenerForContextOwnerRemoved:
  798. // Send a message to remove a listener since this is the last listener
  799. // for the context owner (e.g., extension).
  800. ipc_message_sender_->SendRemoveUnfilteredEventListenerIPC(script_context,
  801. event_name);
  802. // Check if we need to remove a lazy listener as well.
  803. [[fallthrough]];
  804. case binding::EventListenersChanged::
  805. kLastUnfilteredListenerForContextRemoved: {
  806. // If the listener was the last for the event page, we need to remove
  807. // the lazy listener entry.
  808. if (is_lazy) {
  809. ipc_message_sender_->SendRemoveUnfilteredLazyEventListenerIPC(
  810. script_context, event_name);
  811. }
  812. break;
  813. }
  814. // TODO(https://crbug.com/873017): This is broken, since we'll only add or
  815. // remove a lazy listener if it was the first/last for the context owner.
  816. // This means that if an extension registers a filtered listener on a page
  817. // and *then* adds one in the event page, we won't properly add the listener
  818. // as lazy. This is an issue for both native and JS bindings, so for now,
  819. // let's settle for parity.
  820. case binding::EventListenersChanged::
  821. kFirstListenerWithFilterForContextOwnerAdded:
  822. DCHECK(filter);
  823. ipc_message_sender_->SendAddFilteredEventListenerIPC(
  824. script_context, event_name, *filter, is_lazy);
  825. break;
  826. case binding::EventListenersChanged::
  827. kLastListenerWithFilterForContextOwnerRemoved:
  828. DCHECK(filter);
  829. ipc_message_sender_->SendRemoveFilteredEventListenerIPC(
  830. script_context, event_name, *filter, is_lazy);
  831. break;
  832. }
  833. }
  834. void NativeExtensionBindingsSystem::GetJSBindingUtil(
  835. v8::Local<v8::Context> context,
  836. v8::Local<v8::Value>* binding_util_out) {
  837. gin::Handle<APIBindingJSUtil> handle = gin::CreateHandle(
  838. context->GetIsolate(),
  839. new APIBindingJSUtil(
  840. api_system_.type_reference_map(), api_system_.request_handler(),
  841. api_system_.event_handler(), api_system_.exception_handler()));
  842. *binding_util_out = handle.ToV8();
  843. }
  844. void NativeExtensionBindingsSystem::UpdateContentCapabilities(
  845. ScriptContext* context) {
  846. Feature::Context context_type = context->context_type();
  847. if (context_type != Feature::WEB_PAGE_CONTEXT &&
  848. context_type != Feature::BLESSED_WEB_PAGE_CONTEXT) {
  849. return;
  850. }
  851. // Must be called on main thread.
  852. DCHECK(!worker_thread_util::IsWorkerThread());
  853. APIPermissionSet permissions;
  854. for (const auto& extension :
  855. *RendererExtensionRegistry::Get()->GetMainThreadExtensionSet()) {
  856. blink::WebLocalFrame* web_frame = context->web_frame();
  857. GURL url = context->url();
  858. // We allow about:blank pages to take on the privileges of their parents if
  859. // they aren't sandboxed.
  860. if (web_frame && !web_frame->GetSecurityOrigin().IsOpaque()) {
  861. url = ScriptContext::GetEffectiveDocumentURLForContext(web_frame, url,
  862. true);
  863. }
  864. const ContentCapabilitiesInfo& info =
  865. ContentCapabilitiesInfo::Get(extension.get());
  866. if (info.url_patterns.MatchesURL(url)) {
  867. APIPermissionSet new_permissions;
  868. APIPermissionSet::Union(permissions, info.permissions, &new_permissions);
  869. permissions = std::move(new_permissions);
  870. }
  871. }
  872. context->set_content_capabilities(std::move(permissions));
  873. }
  874. void NativeExtensionBindingsSystem::InvalidateFeatureCache(
  875. const ExtensionId& extension_id) {
  876. feature_cache_.InvalidateExtension(extension_id);
  877. }
  878. void NativeExtensionBindingsSystem::SetScriptingParams(ScriptContext* context) {
  879. if (!IsAPIFeatureAvailable(context->v8_context(), "scripting.globalParams"))
  880. return;
  881. v8::Local<v8::Object> scripting_object =
  882. GetAPIHelper(context->v8_context(),
  883. gin::StringToSymbol(context->isolate(), "scripting"));
  884. if (scripting_object.IsEmpty())
  885. return;
  886. // Set the globalParams property on the chrome.scripting object if available.
  887. scripting_object
  888. ->CreateDataProperty(
  889. context->v8_context(),
  890. gin::StringToSymbol(context->isolate(), "globalParams"),
  891. gin::DataObjectBuilder(context->isolate()).Build())
  892. .Check();
  893. }
  894. } // namespace extensions