| // Copyright 2013 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/browser/extension_function.h" |
| |
| #include <utility> |
| |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/singleton.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/metrics/sparse_histogram.h" |
| #include "base/metrics/user_metrics.h" |
| #include "base/synchronization/lock.h" |
| #include "content/public/browser/notification_source.h" |
| #include "content/public/browser/notification_types.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/browser/web_contents_observer.h" |
| #include "extensions/browser/bad_message.h" |
| #include "extensions/browser/extension_function_dispatcher.h" |
| #include "extensions/browser/extension_message_filter.h" |
| #include "extensions/browser/extensions_browser_client.h" |
| #include "extensions/browser/io_thread_extension_message_filter.h" |
| #include "extensions/common/error_utils.h" |
| #include "extensions/common/extension_api.h" |
| #include "extensions/common/extension_messages.h" |
| |
| using content::BrowserThread; |
| using content::RenderViewHost; |
| using content::WebContents; |
| using extensions::ErrorUtils; |
| using extensions::ExtensionAPI; |
| using extensions::Feature; |
| |
| namespace { |
| |
| // Logs UMA about the performance for a given extension function run. |
| void LogUma(bool success, |
| base::TimeDelta elapsed_time, |
| extensions::functions::HistogramValue histogram_value) { |
| // Note: Certain functions perform actions that are inherently slow - such as |
| // anything waiting on user action. As such, we can't always assume that a |
| // long execution time equates to a poorly-performing function. |
| if (success) { |
| if (elapsed_time < base::TimeDelta::FromMilliseconds(1)) { |
| UMA_HISTOGRAM_SPARSE_SLOWLY( |
| "Extensions.Functions.SucceededTime.LessThan1ms", histogram_value); |
| } else if (elapsed_time < base::TimeDelta::FromMilliseconds(5)) { |
| UMA_HISTOGRAM_SPARSE_SLOWLY("Extensions.Functions.SucceededTime.1msTo5ms", |
| histogram_value); |
| } else if (elapsed_time < base::TimeDelta::FromMilliseconds(10)) { |
| UMA_HISTOGRAM_SPARSE_SLOWLY( |
| "Extensions.Functions.SucceededTime.5msTo10ms", histogram_value); |
| } else { |
| UMA_HISTOGRAM_SPARSE_SLOWLY("Extensions.Functions.SucceededTime.Over10ms", |
| histogram_value); |
| } |
| UMA_HISTOGRAM_TIMES("Extensions.Functions.SucceededTotalExecutionTime", |
| elapsed_time); |
| } else { |
| if (elapsed_time < base::TimeDelta::FromMilliseconds(1)) { |
| UMA_HISTOGRAM_SPARSE_SLOWLY("Extensions.Functions.FailedTime.LessThan1ms", |
| histogram_value); |
| } else if (elapsed_time < base::TimeDelta::FromMilliseconds(5)) { |
| UMA_HISTOGRAM_SPARSE_SLOWLY("Extensions.Functions.FailedTime.1msTo5ms", |
| histogram_value); |
| } else if (elapsed_time < base::TimeDelta::FromMilliseconds(10)) { |
| UMA_HISTOGRAM_SPARSE_SLOWLY("Extensions.Functions.FailedTime.5msTo10ms", |
| histogram_value); |
| } else { |
| UMA_HISTOGRAM_SPARSE_SLOWLY("Extensions.Functions.FailedTime.Over10ms", |
| histogram_value); |
| } |
| UMA_HISTOGRAM_TIMES("Extensions.Functions.FailedTotalExecutionTime", |
| elapsed_time); |
| } |
| } |
| |
| void LogBadMessage(extensions::functions::HistogramValue histogram_value) { |
| base::RecordAction(base::UserMetricsAction("BadMessageTerminate_EFD")); |
| // Track the specific function's |histogram_value|, as this may indicate a |
| // bug in that API's implementation. |
| UMA_HISTOGRAM_ENUMERATION("Extensions.BadMessageFunctionName", |
| histogram_value, |
| extensions::functions::ENUM_BOUNDARY); |
| } |
| |
| template <class T> |
| void ReceivedBadMessage(T* bad_message_sender, |
| extensions::bad_message::BadMessageReason reason, |
| extensions::functions::HistogramValue histogram_value) { |
| LogBadMessage(histogram_value); |
| // The renderer has done validation before sending extension api requests. |
| // Therefore, we should never receive a request that is invalid in a way |
| // that JSON validation in the renderer should have caught. It could be an |
| // attacker trying to exploit the browser, so we crash the renderer instead. |
| extensions::bad_message::ReceivedBadMessage(bad_message_sender, reason); |
| } |
| |
| class ArgumentListResponseValue |
| : public ExtensionFunction::ResponseValueObject { |
| public: |
| ArgumentListResponseValue(ExtensionFunction* function, |
| std::unique_ptr<base::ListValue> result) { |
| SetFunctionResults(function, std::move(result)); |
| // It would be nice to DCHECK(error.empty()) but some legacy extension |
| // function implementations... I'm looking at chrome.input.ime... do this |
| // for some reason. |
| } |
| |
| ~ArgumentListResponseValue() override {} |
| |
| bool Apply() override { return true; } |
| }; |
| |
| class ErrorWithArgumentsResponseValue : public ArgumentListResponseValue { |
| public: |
| ErrorWithArgumentsResponseValue(ExtensionFunction* function, |
| std::unique_ptr<base::ListValue> result, |
| const std::string& error) |
| : ArgumentListResponseValue(function, std::move(result)) { |
| SetFunctionError(function, error); |
| } |
| |
| ~ErrorWithArgumentsResponseValue() override {} |
| |
| bool Apply() override { return false; } |
| }; |
| |
| class ErrorResponseValue : public ExtensionFunction::ResponseValueObject { |
| public: |
| ErrorResponseValue(ExtensionFunction* function, const std::string& error) { |
| // It would be nice to DCHECK(!error.empty()) but too many legacy extension |
| // function implementations don't set error but signal failure. |
| SetFunctionError(function, error); |
| } |
| |
| ~ErrorResponseValue() override {} |
| |
| bool Apply() override { return false; } |
| }; |
| |
| class BadMessageResponseValue : public ExtensionFunction::ResponseValueObject { |
| public: |
| explicit BadMessageResponseValue(ExtensionFunction* function) { |
| function->SetBadMessage(); |
| NOTREACHED() << function->name() << ": bad message"; |
| } |
| |
| ~BadMessageResponseValue() override {} |
| |
| bool Apply() override { return false; } |
| }; |
| |
| class RespondNowAction : public ExtensionFunction::ResponseActionObject { |
| public: |
| typedef base::Callback<void(bool)> SendResponseCallback; |
| RespondNowAction(ExtensionFunction::ResponseValue result, |
| const SendResponseCallback& send_response) |
| : result_(std::move(result)), send_response_(send_response) {} |
| ~RespondNowAction() override {} |
| |
| void Execute() override { send_response_.Run(result_->Apply()); } |
| |
| private: |
| ExtensionFunction::ResponseValue result_; |
| SendResponseCallback send_response_; |
| }; |
| |
| class RespondLaterAction : public ExtensionFunction::ResponseActionObject { |
| public: |
| ~RespondLaterAction() override {} |
| |
| void Execute() override {} |
| }; |
| |
| class AlreadyRespondedAction : public ExtensionFunction::ResponseActionObject { |
| public: |
| ~AlreadyRespondedAction() override {} |
| |
| void Execute() override {} |
| }; |
| |
| // Used in implementation of ScopedUserGestureForTests. |
| class UserGestureForTests { |
| public: |
| static UserGestureForTests* GetInstance(); |
| |
| // Returns true if there is at least one ScopedUserGestureForTests object |
| // alive. |
| bool HaveGesture(); |
| |
| // These should be called when a ScopedUserGestureForTests object is |
| // created/destroyed respectively. |
| void IncrementCount(); |
| void DecrementCount(); |
| |
| private: |
| UserGestureForTests(); |
| friend struct base::DefaultSingletonTraits<UserGestureForTests>; |
| |
| base::Lock lock_; // for protecting access to |count_| |
| int count_; |
| }; |
| |
| // static |
| UserGestureForTests* UserGestureForTests::GetInstance() { |
| return base::Singleton<UserGestureForTests>::get(); |
| } |
| |
| UserGestureForTests::UserGestureForTests() : count_(0) {} |
| |
| bool UserGestureForTests::HaveGesture() { |
| base::AutoLock autolock(lock_); |
| return count_ > 0; |
| } |
| |
| void UserGestureForTests::IncrementCount() { |
| base::AutoLock autolock(lock_); |
| ++count_; |
| } |
| |
| void UserGestureForTests::DecrementCount() { |
| base::AutoLock autolock(lock_); |
| --count_; |
| } |
| |
| |
| } // namespace |
| |
| void ExtensionFunction::ResponseValueObject::SetFunctionResults( |
| ExtensionFunction* function, |
| std::unique_ptr<base::ListValue> results) { |
| DCHECK(!function->results_) << "Function " << function->name_ |
| << "already has results set."; |
| function->results_ = std::move(results); |
| } |
| |
| void ExtensionFunction::ResponseValueObject::SetFunctionError( |
| ExtensionFunction* function, |
| const std::string& error) { |
| DCHECK(function->error_.empty()) << "Function " << function->name_ |
| << "already has an error."; |
| function->error_ = error; |
| } |
| |
| // static |
| bool ExtensionFunction::ignore_all_did_respond_for_testing_do_not_use = false; |
| |
| // static |
| const char ExtensionFunction::kUnknownErrorDoNotUse[] = "Unknown error."; |
| |
| // static |
| void ExtensionFunctionDeleteTraits::Destruct(const ExtensionFunction* x) { |
| x->Destruct(); |
| } |
| |
| // Helper class to track the lifetime of ExtensionFunction's RenderFrameHost and |
| // notify the function when it is deleted, as well as forwarding any messages |
| // to the ExtensionFunction. |
| class UIThreadExtensionFunction::RenderFrameHostTracker |
| : public content::WebContentsObserver { |
| public: |
| explicit RenderFrameHostTracker(UIThreadExtensionFunction* function) |
| : content::WebContentsObserver( |
| WebContents::FromRenderFrameHost(function->render_frame_host())), |
| function_(function) { |
| } |
| |
| private: |
| // content::WebContentsObserver: |
| void RenderFrameDeleted( |
| content::RenderFrameHost* render_frame_host) override { |
| if (render_frame_host == function_->render_frame_host()) |
| function_->SetRenderFrameHost(nullptr); |
| } |
| |
| bool OnMessageReceived(const IPC::Message& message, |
| content::RenderFrameHost* render_frame_host) override { |
| return render_frame_host == function_->render_frame_host() && |
| function_->OnMessageReceived(message); |
| } |
| |
| UIThreadExtensionFunction* function_; // Owns us. |
| |
| DISALLOW_COPY_AND_ASSIGN(RenderFrameHostTracker); |
| }; |
| |
| ExtensionFunction::ExtensionFunction() |
| : request_id_(-1), |
| profile_id_(NULL), |
| name_(""), |
| has_callback_(false), |
| include_incognito_(false), |
| user_gesture_(false), |
| bad_message_(false), |
| histogram_value_(extensions::functions::UNKNOWN), |
| source_context_type_(Feature::UNSPECIFIED_CONTEXT), |
| source_process_id_(-1), |
| did_respond_(false) {} |
| |
| ExtensionFunction::~ExtensionFunction() { |
| } |
| |
| UIThreadExtensionFunction* ExtensionFunction::AsUIThreadExtensionFunction() { |
| return NULL; |
| } |
| |
| IOThreadExtensionFunction* ExtensionFunction::AsIOThreadExtensionFunction() { |
| return NULL; |
| } |
| |
| bool ExtensionFunction::HasPermission() { |
| Feature::Availability availability = |
| ExtensionAPI::GetSharedInstance()->IsAvailable( |
| name_, extension_.get(), source_context_type_, source_url(), |
| extensions::CheckAliasStatus::ALLOWED); |
| return availability.is_available(); |
| } |
| |
| void ExtensionFunction::OnQuotaExceeded(const std::string& violation_error) { |
| error_ = violation_error; |
| SendResponseImpl(false); |
| } |
| |
| void ExtensionFunction::SetArgs(const base::ListValue* args) { |
| DCHECK(!args_.get()); // Should only be called once. |
| args_ = args->CreateDeepCopy(); |
| } |
| |
| const base::ListValue* ExtensionFunction::GetResultList() const { |
| return results_.get(); |
| } |
| |
| const std::string& ExtensionFunction::GetError() const { |
| return error_; |
| } |
| |
| void ExtensionFunction::SetBadMessage() { |
| bad_message_ = true; |
| } |
| |
| bool ExtensionFunction::user_gesture() const { |
| return user_gesture_ || UserGestureForTests::GetInstance()->HaveGesture(); |
| } |
| |
| ExtensionFunction::ResponseValue ExtensionFunction::NoArguments() { |
| return ResponseValue( |
| new ArgumentListResponseValue(this, std::make_unique<base::ListValue>())); |
| } |
| |
| ExtensionFunction::ResponseValue ExtensionFunction::OneArgument( |
| std::unique_ptr<base::Value> arg) { |
| std::unique_ptr<base::ListValue> args(new base::ListValue()); |
| args->Append(std::move(arg)); |
| return ResponseValue(new ArgumentListResponseValue(this, std::move(args))); |
| } |
| |
| ExtensionFunction::ResponseValue ExtensionFunction::TwoArguments( |
| std::unique_ptr<base::Value> arg1, |
| std::unique_ptr<base::Value> arg2) { |
| std::unique_ptr<base::ListValue> args(new base::ListValue()); |
| args->Append(std::move(arg1)); |
| args->Append(std::move(arg2)); |
| return ResponseValue(new ArgumentListResponseValue(this, std::move(args))); |
| } |
| |
| ExtensionFunction::ResponseValue ExtensionFunction::ArgumentList( |
| std::unique_ptr<base::ListValue> args) { |
| return ResponseValue(new ArgumentListResponseValue(this, std::move(args))); |
| } |
| |
| ExtensionFunction::ResponseValue ExtensionFunction::Error( |
| const std::string& error) { |
| return ResponseValue(new ErrorResponseValue(this, error)); |
| } |
| |
| ExtensionFunction::ResponseValue ExtensionFunction::Error( |
| const std::string& format, |
| const std::string& s1) { |
| return ResponseValue( |
| new ErrorResponseValue(this, ErrorUtils::FormatErrorMessage(format, s1))); |
| } |
| |
| ExtensionFunction::ResponseValue ExtensionFunction::Error( |
| const std::string& format, |
| const std::string& s1, |
| const std::string& s2) { |
| return ResponseValue(new ErrorResponseValue( |
| this, ErrorUtils::FormatErrorMessage(format, s1, s2))); |
| } |
| |
| ExtensionFunction::ResponseValue ExtensionFunction::Error( |
| const std::string& format, |
| const std::string& s1, |
| const std::string& s2, |
| const std::string& s3) { |
| return ResponseValue(new ErrorResponseValue( |
| this, ErrorUtils::FormatErrorMessage(format, s1, s2, s3))); |
| } |
| |
| ExtensionFunction::ResponseValue ExtensionFunction::ErrorWithArguments( |
| std::unique_ptr<base::ListValue> args, |
| const std::string& error) { |
| return ResponseValue( |
| new ErrorWithArgumentsResponseValue(this, std::move(args), error)); |
| } |
| |
| ExtensionFunction::ResponseValue ExtensionFunction::BadMessage() { |
| return ResponseValue(new BadMessageResponseValue(this)); |
| } |
| |
| ExtensionFunction::ResponseAction ExtensionFunction::RespondNow( |
| ResponseValue result) { |
| return ResponseAction(new RespondNowAction( |
| std::move(result), |
| base::Bind(&ExtensionFunction::SendResponseImpl, this))); |
| } |
| |
| ExtensionFunction::ResponseAction ExtensionFunction::RespondLater() { |
| return ResponseAction(new RespondLaterAction()); |
| } |
| |
| ExtensionFunction::ResponseAction ExtensionFunction::AlreadyResponded() { |
| DCHECK(did_respond()) << "ExtensionFunction did not call Respond()," |
| " but Run() returned AlreadyResponded()"; |
| return ResponseAction(new AlreadyRespondedAction()); |
| } |
| |
| // static |
| ExtensionFunction::ResponseAction ExtensionFunction::ValidationFailure( |
| ExtensionFunction* function) { |
| return function->RespondNow(function->BadMessage()); |
| } |
| |
| void ExtensionFunction::Respond(ResponseValue result) { |
| SendResponseImpl(result->Apply()); |
| } |
| |
| bool ExtensionFunction::PreRunValidation(std::string* error) { |
| return true; |
| } |
| |
| ExtensionFunction::ResponseAction ExtensionFunction::RunWithValidation() { |
| std::string error; |
| if (!PreRunValidation(&error)) { |
| DCHECK(!error.empty() || bad_message_); |
| return bad_message_ ? ValidationFailure(this) : RespondNow(Error(error)); |
| } |
| return Run(); |
| } |
| |
| bool ExtensionFunction::ShouldSkipQuotaLimiting() const { |
| return false; |
| } |
| |
| bool ExtensionFunction::HasOptionalArgument(size_t index) { |
| base::Value* value; |
| return args_->Get(index, &value) && !value->IsType(base::Value::Type::NONE); |
| } |
| |
| void ExtensionFunction::SendResponseImpl(bool success) { |
| DCHECK(!response_callback_.is_null()); |
| DCHECK(!did_respond_) << name_; |
| did_respond_ = true; |
| |
| ResponseType response = success ? SUCCEEDED : FAILED; |
| if (bad_message_) { |
| response = BAD_MESSAGE; |
| LOG(ERROR) << "Bad extension message " << name_; |
| } |
| response_type_ = std::make_unique<ResponseType>(response); |
| |
| // If results were never set, we send an empty argument list. |
| if (!results_) |
| results_.reset(new base::ListValue()); |
| |
| response_callback_.Run(response, *results_, GetError(), histogram_value()); |
| LogUma(success, timer_.Elapsed(), histogram_value_); |
| |
| OnResponded(); |
| } |
| |
| UIThreadExtensionFunction::UIThreadExtensionFunction() |
| : context_(nullptr), |
| render_frame_host_(nullptr), |
| service_worker_version_id_(extensions::kInvalidServiceWorkerVersionId) {} |
| |
| UIThreadExtensionFunction::~UIThreadExtensionFunction() { |
| if (dispatcher() && (render_frame_host() || is_from_service_worker())) { |
| dispatcher()->OnExtensionFunctionCompleted(extension(), |
| is_from_service_worker()); |
| } |
| |
| // The extension function should always respond to avoid leaks in the |
| // renderer, dangling callbacks, etc. The exception is if the system is |
| // shutting down. |
| // TODO(devlin): Duplicate this check in IOThreadExtensionFunction. It's |
| // tricky because checking IsShuttingDown has to be called from the UI thread. |
| extensions::ExtensionsBrowserClient* browser_client = |
| extensions::ExtensionsBrowserClient::Get(); |
| DCHECK(!browser_client || browser_client->IsShuttingDown() || did_respond() || |
| ignore_all_did_respond_for_testing_do_not_use) |
| << name(); |
| } |
| |
| UIThreadExtensionFunction* |
| UIThreadExtensionFunction::AsUIThreadExtensionFunction() { |
| return this; |
| } |
| |
| bool UIThreadExtensionFunction::PreRunValidation(std::string* error) { |
| if (!ExtensionFunction::PreRunValidation(error)) |
| return false; |
| |
| // TODO(crbug.com/625646) This is a partial fix to avoid crashes when certain |
| // extension functions run during shutdown. Browser or Notification creation |
| // for example create a ScopedKeepAlive, which hit a CHECK if the browser is |
| // shutting down. This fixes the current problem as the known issues happen |
| // through synchronous calls from Run(), but posted tasks will not be covered. |
| // A possible fix would involve refactoring ExtensionFunction: unrefcount |
| // here and use weakptrs for the tasks, then have it owned by something that |
| // will be destroyed naturally in the course of shut down. |
| if (extensions::ExtensionsBrowserClient::Get()->IsShuttingDown()) { |
| *error = "The browser is shutting down."; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void UIThreadExtensionFunction::SetBadMessage() { |
| ExtensionFunction::SetBadMessage(); |
| |
| if (render_frame_host()) { |
| ReceivedBadMessage(render_frame_host()->GetProcess(), |
| is_from_service_worker() |
| ? extensions::bad_message::EFD_BAD_MESSAGE_WORKER |
| : extensions::bad_message::EFD_BAD_MESSAGE, |
| histogram_value()); |
| } |
| } |
| |
| bool UIThreadExtensionFunction::OnMessageReceived(const IPC::Message& message) { |
| return false; |
| } |
| |
| void UIThreadExtensionFunction::Destruct() const { |
| BrowserThread::DeleteOnUIThread::Destruct(this); |
| } |
| |
| void UIThreadExtensionFunction::SetRenderFrameHost( |
| content::RenderFrameHost* render_frame_host) { |
| // An extension function from Service Worker does not have a RenderFrameHost. |
| if (is_from_service_worker()) { |
| DCHECK(!render_frame_host); |
| return; |
| } |
| |
| DCHECK_NE(render_frame_host_ == nullptr, render_frame_host == nullptr); |
| render_frame_host_ = render_frame_host; |
| tracker_.reset( |
| render_frame_host ? new RenderFrameHostTracker(this) : nullptr); |
| } |
| |
| content::WebContents* UIThreadExtensionFunction::GetAssociatedWebContents() { |
| content::WebContents* web_contents = NULL; |
| if (dispatcher()) |
| web_contents = dispatcher()->GetAssociatedWebContents(); |
| |
| return web_contents; |
| } |
| |
| content::WebContents* UIThreadExtensionFunction::GetSenderWebContents() { |
| return render_frame_host_ ? |
| content::WebContents::FromRenderFrameHost(render_frame_host_) : nullptr; |
| } |
| |
| void UIThreadExtensionFunction::OnResponded() { |
| if (!transferred_blob_uuids_.empty()) { |
| render_frame_host_->Send( |
| new ExtensionMsg_TransferBlobs(transferred_blob_uuids_)); |
| } |
| } |
| |
| void UIThreadExtensionFunction::SetTransferredBlobUUIDs( |
| const std::vector<std::string>& blob_uuids) { |
| DCHECK(transferred_blob_uuids_.empty()); // Should only be called once. |
| transferred_blob_uuids_ = blob_uuids; |
| } |
| |
| void UIThreadExtensionFunction::WriteToConsole( |
| content::ConsoleMessageLevel level, |
| const std::string& message) { |
| // Only the main frame handles dev tools messages. |
| WebContents::FromRenderFrameHost(render_frame_host_) |
| ->GetMainFrame() |
| ->AddMessageToConsole(level, message); |
| } |
| |
| IOThreadExtensionFunction::IOThreadExtensionFunction() |
| : routing_id_(MSG_ROUTING_NONE) { |
| } |
| |
| IOThreadExtensionFunction::~IOThreadExtensionFunction() { |
| } |
| |
| IOThreadExtensionFunction* |
| IOThreadExtensionFunction::AsIOThreadExtensionFunction() { |
| return this; |
| } |
| |
| void IOThreadExtensionFunction::SetBadMessage() { |
| ExtensionFunction::SetBadMessage(); |
| if (ipc_sender_) { |
| ReceivedBadMessage( |
| static_cast<content::BrowserMessageFilter*>(ipc_sender_.get()), |
| extensions::bad_message::EFD_BAD_MESSAGE, histogram_value()); |
| } |
| } |
| |
| void IOThreadExtensionFunction::Destruct() const { |
| BrowserThread::DeleteOnIOThread::Destruct(this); |
| } |
| |
| AsyncExtensionFunction::AsyncExtensionFunction() { |
| } |
| |
| void AsyncExtensionFunction::SetError(const std::string& error) { |
| error_ = error; |
| } |
| |
| const std::string& AsyncExtensionFunction::GetError() const { |
| return error_.empty() ? UIThreadExtensionFunction::GetError() : error_; |
| } |
| |
| AsyncExtensionFunction::~AsyncExtensionFunction() { |
| } |
| |
| void AsyncExtensionFunction::SetResult(std::unique_ptr<base::Value> result) { |
| results_.reset(new base::ListValue()); |
| results_->Append(std::move(result)); |
| } |
| |
| void AsyncExtensionFunction::SetResultList( |
| std::unique_ptr<base::ListValue> results) { |
| results_ = std::move(results); |
| } |
| |
| ExtensionFunction::ScopedUserGestureForTests::ScopedUserGestureForTests() { |
| UserGestureForTests::GetInstance()->IncrementCount(); |
| } |
| |
| ExtensionFunction::ScopedUserGestureForTests::~ScopedUserGestureForTests() { |
| UserGestureForTests::GetInstance()->DecrementCount(); |
| } |
| |
| ExtensionFunction::ResponseAction AsyncExtensionFunction::Run() { |
| if (RunAsync()) |
| return RespondLater(); |
| DCHECK(!results_); |
| return RespondNow(Error(error_)); |
| } |
| |
| // static |
| bool AsyncExtensionFunction::ValidationFailure( |
| AsyncExtensionFunction* function) { |
| return false; |
| } |
| |
| void AsyncExtensionFunction::SendResponse(bool success) { |
| ResponseValue response; |
| if (success) { |
| response = ArgumentList(std::move(results_)); |
| } else { |
| response = results_ ? ErrorWithArguments(std::move(results_), error_) |
| : Error(error_); |
| } |
| Respond(std::move(response)); |
| } |