// Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "extensions/renderer/extension_interaction_provider.h" #include "base/feature_list.h" #include "base/memory/ptr_util.h" #include "content/public/common/content_features.h" #include "extensions/renderer/bindings/get_per_context_data.h" #include "extensions/renderer/get_script_context.h" #include "extensions/renderer/script_context.h" #include "extensions/renderer/worker_thread_util.h" #include "third_party/blink/public/web/web_local_frame.h" namespace extensions { namespace { struct ExtensionInteractionData : public base::SupportsUserData::Data { static constexpr char kPerContextDataKey[] = "extension_interaction"; int interaction_count = 0; int token_interaction_count = 0; }; constexpr char ExtensionInteractionData::kPerContextDataKey[]; } // namespace // ExtensionInteractionProvider::Token ----------------------------------------- ExtensionInteractionProvider::Token::Token(bool for_worker) : is_for_service_worker_(for_worker) {} ExtensionInteractionProvider::Token::~Token() {} // ExtensionInteractionProvider::Scope ----------------------------------------- ExtensionInteractionProvider::Scope::Scope() {} ExtensionInteractionProvider::Scope::~Scope() {} // static. std::unique_ptr ExtensionInteractionProvider::Scope::ForWorker( v8::Local v8_context) { DCHECK(worker_thread_util::IsWorkerThread()); auto scope = base::WrapUnique(new Scope()); scope->worker_thread_interaction_ = std::make_unique(v8_context, false); return scope; } // static. std::unique_ptr ExtensionInteractionProvider::Scope::ForToken( v8::Local v8_context, std::unique_ptr token) { Token* token_impl = static_cast(token.get()); if (!token_impl->is_for_service_worker()) { // UserActivationV2 replaces the concept of (scoped) tokens with a // frame-wide state, hence skips token forwarding. return nullptr; } auto scope = base::WrapUnique(new Scope()); scope->worker_thread_interaction_ = std::make_unique(v8_context, true); return scope; } ExtensionInteractionProvider::Scope::ScopedWorkerInteraction:: ScopedWorkerInteraction(v8::Local v8_context, bool created_from_token) : v8_context_(v8_context), created_from_token_(created_from_token) { ExtensionInteractionData* per_context_data = GetPerContextData(v8_context, kCreateIfMissing); DCHECK(per_context_data); if (created_from_token_) per_context_data->token_interaction_count++; else per_context_data->interaction_count++; } ExtensionInteractionProvider::Scope::ScopedWorkerInteraction:: ~ScopedWorkerInteraction() { ExtensionInteractionData* per_context_data = GetPerContextData(v8_context_, kDontCreateIfMissing); // If |v8_context_| was invalidated (e.g. because of JS running), bail out. if (!per_context_data) return; if (created_from_token_) { DCHECK_GT(per_context_data->token_interaction_count, 0); per_context_data->token_interaction_count--; } else { DCHECK_GT(per_context_data->interaction_count, 0); per_context_data->interaction_count--; } } // ExtensionInteractionProvider ------------------------------------------------ ExtensionInteractionProvider::ExtensionInteractionProvider() = default; ExtensionInteractionProvider::~ExtensionInteractionProvider() = default; // static bool ExtensionInteractionProvider::HasActiveExtensionInteraction( v8::Local v8_context) { // Service Worker based context: if (worker_thread_util::IsWorkerThread()) { ExtensionInteractionData* per_context_data = GetPerContextData(v8_context, kDontCreateIfMissing); if (per_context_data && (per_context_data->interaction_count > 0 || per_context_data->token_interaction_count > 0)) { return true; } return worker_thread_util::HasWorkerContextProxyInteraction(); } // RenderFrame based context: ScriptContext* script_context = GetScriptContextFromV8ContextChecked(v8_context); if (!script_context->web_frame()) return false; return script_context->web_frame()->HasTransientUserActivation(); } std::unique_ptr ExtensionInteractionProvider::GetCurrentToken( v8::Local v8_context) const { if (worker_thread_util::IsWorkerThread()) { ExtensionInteractionData* per_context_data = GetPerContextData(v8_context, kDontCreateIfMissing); const bool has_extension_api_interaction = per_context_data && per_context_data->interaction_count > 0; // Only create token for Service Workers when we have an interaction taking // place that wasn't created through another token (i.e. do not look at // worker_data->token_interaction_count). if (!has_extension_api_interaction && !worker_thread_util::HasWorkerContextProxyInteraction()) { return nullptr; } return base::WrapUnique(new Token(true)); } // Render frame based token. return base::WrapUnique(new Token(false)); } std::unique_ptr ExtensionInteractionProvider::CreateScopedInteraction( v8::Local v8_context, std::unique_ptr token) const { return Scope::ForToken(v8_context, std::move(token)); } bool ExtensionInteractionProvider::HasActiveInteraction( v8::Local v8_context) const { return ExtensionInteractionProvider::HasActiveExtensionInteraction( v8_context); } } // namespace extensions