blob: 80b6cf8113bdead93f36653ece9ef8c6684657aa [file] [log] [blame]
Istiaque Ahmed96f506092019-07-08 23:40:571// Copyright 2019 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/extension_interaction_provider.h"
6
7#include "base/feature_list.h"
8#include "base/memory/ptr_util.h"
9#include "content/public/common/content_features.h"
10#include "extensions/renderer/bindings/get_per_context_data.h"
11#include "extensions/renderer/get_script_context.h"
12#include "extensions/renderer/script_context.h"
13#include "extensions/renderer/worker_thread_util.h"
14#include "third_party/blink/public/web/web_local_frame.h"
Istiaque Ahmed96f506092019-07-08 23:40:5715
16namespace extensions {
17
18namespace {
19struct ExtensionInteractionData : public base::SupportsUserData::Data {
20 static constexpr char kPerContextDataKey[] = "extension_interaction";
21 int interaction_count = 0;
22 int token_interaction_count = 0;
23};
24constexpr char ExtensionInteractionData::kPerContextDataKey[];
25
26} // namespace
27
28// ExtensionInteractionProvider::Token -----------------------------------------
29ExtensionInteractionProvider::Token::Token(bool for_worker)
Mustaq Ahmedd4d2cef2019-11-19 13:03:5430 : is_for_service_worker_(for_worker) {}
Istiaque Ahmed96f506092019-07-08 23:40:5731ExtensionInteractionProvider::Token::~Token() {}
32
33// ExtensionInteractionProvider::Scope -----------------------------------------
34
35ExtensionInteractionProvider::Scope::Scope() {}
36ExtensionInteractionProvider::Scope::~Scope() {}
37
38// static.
39std::unique_ptr<ExtensionInteractionProvider::Scope>
40ExtensionInteractionProvider::Scope::ForWorker(
41 v8::Local<v8::Context> v8_context) {
42 DCHECK(worker_thread_util::IsWorkerThread());
43 auto scope = base::WrapUnique(new Scope());
44 scope->worker_thread_interaction_ =
45 std::make_unique<ScopedWorkerInteraction>(v8_context, false);
46 return scope;
47}
48
49// static.
50std::unique_ptr<ExtensionInteractionProvider::Scope>
Istiaque Ahmed96f506092019-07-08 23:40:5751ExtensionInteractionProvider::Scope::ForToken(
52 v8::Local<v8::Context> v8_context,
53 std::unique_ptr<InteractionProvider::Token> token) {
54 Token* token_impl = static_cast<Token*>(token.get());
Mustaq Ahmede5f12562019-10-30 18:02:0355 if (!token_impl->is_for_service_worker()) {
Istiaque Ahmed96f506092019-07-08 23:40:5756 // UserActivationV2 replaces the concept of (scoped) tokens with a
57 // frame-wide state, hence skips token forwarding.
58 return nullptr;
59 }
60
61 auto scope = base::WrapUnique(new Scope());
Mustaq Ahmedd4d2cef2019-11-19 13:03:5462 scope->worker_thread_interaction_ =
63 std::make_unique<ScopedWorkerInteraction>(v8_context, true);
Istiaque Ahmed96f506092019-07-08 23:40:5764 return scope;
65}
66
67ExtensionInteractionProvider::Scope::ScopedWorkerInteraction::
68 ScopedWorkerInteraction(v8::Local<v8::Context> v8_context,
69 bool created_from_token)
70 : v8_context_(v8_context), created_from_token_(created_from_token) {
71 ExtensionInteractionData* per_context_data =
72 GetPerContextData<ExtensionInteractionData>(v8_context, kCreateIfMissing);
73 DCHECK(per_context_data);
74 if (created_from_token_)
75 per_context_data->token_interaction_count++;
76 else
77 per_context_data->interaction_count++;
78}
79ExtensionInteractionProvider::Scope::ScopedWorkerInteraction::
80 ~ScopedWorkerInteraction() {
81 ExtensionInteractionData* per_context_data =
82 GetPerContextData<ExtensionInteractionData>(v8_context_,
83 kDontCreateIfMissing);
84 // If |v8_context_| was invalidated (e.g. because of JS running), bail out.
85 if (!per_context_data)
86 return;
87
88 if (created_from_token_) {
89 DCHECK_GT(per_context_data->token_interaction_count, 0);
90 per_context_data->token_interaction_count--;
91 } else {
92 DCHECK_GT(per_context_data->interaction_count, 0);
93 per_context_data->interaction_count--;
94 }
95}
96
97// ExtensionInteractionProvider ------------------------------------------------
98
99ExtensionInteractionProvider::ExtensionInteractionProvider() = default;
100
101ExtensionInteractionProvider::~ExtensionInteractionProvider() = default;
102
103// static
104bool ExtensionInteractionProvider::HasActiveExtensionInteraction(
105 v8::Local<v8::Context> v8_context) {
106 // Service Worker based context:
107 if (worker_thread_util::IsWorkerThread()) {
108 ExtensionInteractionData* per_context_data =
109 GetPerContextData<ExtensionInteractionData>(v8_context,
110 kDontCreateIfMissing);
111 if (per_context_data && (per_context_data->interaction_count > 0 ||
112 per_context_data->token_interaction_count > 0)) {
113 return true;
114 }
115 return worker_thread_util::HasWorkerContextProxyInteraction();
116 }
117
118 // RenderFrame based context:
119 ScriptContext* script_context =
120 GetScriptContextFromV8ContextChecked(v8_context);
Mustaq Ahmed4baa9a6e82019-12-20 23:43:46121 if (!script_context->web_frame())
122 return false;
123 return script_context->web_frame()->HasTransientUserActivation();
Istiaque Ahmed96f506092019-07-08 23:40:57124}
125
126std::unique_ptr<InteractionProvider::Token>
127ExtensionInteractionProvider::GetCurrentToken(
128 v8::Local<v8::Context> v8_context) const {
129 if (worker_thread_util::IsWorkerThread()) {
130 ExtensionInteractionData* per_context_data =
131 GetPerContextData<ExtensionInteractionData>(v8_context,
132 kDontCreateIfMissing);
133 const bool has_extension_api_interaction =
134 per_context_data && per_context_data->interaction_count > 0;
135 // Only create token for Service Workers when we have an interaction taking
136 // place that wasn't created through another token (i.e. do not look at
137 // worker_data->token_interaction_count).
138 if (!has_extension_api_interaction &&
139 !worker_thread_util::HasWorkerContextProxyInteraction()) {
140 return nullptr;
141 }
142 return base::WrapUnique(new Token(true));
143 }
144
145 // Render frame based token.
146 return base::WrapUnique(new Token(false));
147}
148
149std::unique_ptr<InteractionProvider::Scope>
150ExtensionInteractionProvider::CreateScopedInteraction(
151 v8::Local<v8::Context> v8_context,
152 std::unique_ptr<InteractionProvider::Token> token) const {
153 return Scope::ForToken(v8_context, std::move(token));
154}
155
156bool ExtensionInteractionProvider::HasActiveInteraction(
157 v8::Local<v8::Context> v8_context) const {
158 return ExtensionInteractionProvider::HasActiveExtensionInteraction(
159 v8_context);
160}
161
162} // namespace extensions