cef/libcef/browser/extensions/extension_function_details.cc

292 lines
9.3 KiB
C++

// Copyright 2017 the Chromium Embedded Framework Authors. Portions copyright
// 2014 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 "libcef/browser/extensions/extension_function_details.h"
#include "libcef/browser/browser_context_impl.h"
#include "libcef/browser/extensions/browser_extensions_util.h"
#include "libcef/browser/extensions/extension_system.h"
#include "libcef/browser/thread_util.h"
#include "base/task_scheduler/post_task.h"
#include "chrome/browser/extensions/api/tabs/tabs_constants.h"
#include "chrome/browser/profiles/profile.h"
#include "extensions/browser/extension_function.h"
#include "extensions/browser/extension_function_dispatcher.h"
#include "extensions/common/error_utils.h"
using content::WebContents;
using content::RenderViewHost;
namespace extensions {
namespace keys = extensions::tabs_constants;
namespace {
class CefGetExtensionLoadFileCallbackImpl
: public CefGetExtensionResourceCallback {
public:
CefGetExtensionLoadFileCallbackImpl(
const std::string& file,
CefExtensionFunctionDetails::LoadFileCallback callback)
: file_(file), callback_(std::move(callback)) {}
~CefGetExtensionLoadFileCallbackImpl() {
if (!callback_.is_null()) {
// The callback is still pending. Cancel it now.
if (CEF_CURRENTLY_ON_UIT()) {
RunNow(file_, std::move(callback_), nullptr);
} else {
CEF_POST_TASK(
CEF_UIT,
base::BindOnce(&CefGetExtensionLoadFileCallbackImpl::RunNow, file_,
base::Passed(std::move(callback_)), nullptr));
}
}
}
void Continue(CefRefPtr<CefStreamReader> stream) override {
if (CEF_CURRENTLY_ON_UIT()) {
if (!callback_.is_null()) {
// Always continue asynchronously.
CEF_POST_TASK(
CEF_UIT,
base::BindOnce(&CefGetExtensionLoadFileCallbackImpl::RunNow, file_,
base::Passed(std::move(callback_)), stream));
}
} else {
CEF_POST_TASK(CEF_UIT, base::BindOnce(
&CefGetExtensionLoadFileCallbackImpl::Continue,
this, stream));
}
}
void Cancel() override { Continue(nullptr); }
void Disconnect() { callback_.Reset(); }
private:
static void RunNow(const std::string& file,
CefExtensionFunctionDetails::LoadFileCallback callback,
CefRefPtr<CefStreamReader> stream) {
CEF_REQUIRE_UIT();
if (!stream) {
std::move(callback).Run(nullptr);
return;
}
base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE,
{base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
base::BindOnce(LoadFileFromStream, file, stream), std::move(callback));
}
static std::unique_ptr<std::string> LoadFileFromStream(
const std::string& file,
CefRefPtr<CefStreamReader> stream) {
base::ThreadRestrictions::AssertIOAllowed();
// Move to the end of the stream.
stream->Seek(0, SEEK_END);
const int64 size = stream->Tell();
if (size == 0) {
LOG(WARNING) << "Extension resource " << file << " is empty.";
return nullptr;
}
std::unique_ptr<std::string> result(new std::string());
result->resize(size);
// Move to the beginning of the stream.
stream->Seek(0, SEEK_SET);
// Read all stream contents into the string.
int64 read, offset = 0;
do {
read =
static_cast<int>(stream->Read(&(*result)[offset], 1, size - offset));
offset += read;
} while (read > 0 && offset < size);
if (offset != size) {
LOG(WARNING) << "Extension resource " << file << " read failed; expected "
<< size << ", got " << offset << " bytes.";
return nullptr;
}
return result;
}
const std::string file_;
CefExtensionFunctionDetails::LoadFileCallback callback_;
IMPLEMENT_REFCOUNTING(CefGetExtensionLoadFileCallbackImpl);
DISALLOW_COPY_AND_ASSIGN(CefGetExtensionLoadFileCallbackImpl);
};
} // namespace
CefExtensionFunctionDetails::CefExtensionFunctionDetails(
UIThreadExtensionFunction* function)
: function_(function) {}
CefExtensionFunctionDetails::~CefExtensionFunctionDetails() {}
Profile* CefExtensionFunctionDetails::GetProfile() const {
return Profile::FromBrowserContext(function_->browser_context());
}
CefRefPtr<CefBrowserHostImpl> CefExtensionFunctionDetails::GetSenderBrowser()
const {
content::WebContents* web_contents = function_->GetSenderWebContents();
if (web_contents)
return CefBrowserHostImpl::GetBrowserForContents(web_contents);
return nullptr;
}
CefRefPtr<CefBrowserHostImpl> CefExtensionFunctionDetails::GetCurrentBrowser()
const {
// Start with the browser hosting the extension.
CefRefPtr<CefBrowserHostImpl> browser = GetSenderBrowser();
if (browser && browser->client()) {
CefRefPtr<CefExtensionHandler> handler = GetCefExtension()->GetHandler();
if (handler) {
// Give the handler an opportunity to specify a different browser.
CefRefPtr<CefBrowser> active_browser = handler->GetActiveBrowser(
GetCefExtension(), browser.get(), function_->include_incognito());
if (active_browser && active_browser != browser) {
CefRefPtr<CefBrowserHostImpl> active_browser_impl =
static_cast<CefBrowserHostImpl*>(active_browser.get());
// Make sure we're operating in the same BrowserContextImpl.
if (CefBrowserContextImpl::GetForContext(
browser->GetBrowserContext()) ==
CefBrowserContextImpl::GetForContext(
active_browser_impl->GetBrowserContext())) {
browser = active_browser_impl;
} else {
LOG(WARNING) << "Browser with tabId "
<< active_browser->GetIdentifier()
<< " cannot be accessed because is uses a different "
"CefRequestContext";
}
}
}
}
// May be null during startup/shutdown.
return browser;
}
bool CefExtensionFunctionDetails::CanAccessBrowser(
CefRefPtr<CefBrowserHostImpl> target) const {
DCHECK(target);
// Start with the browser hosting the extension.
CefRefPtr<CefBrowserHostImpl> browser = GetSenderBrowser();
if (browser == target) {
// A sender can always access itself.
return true;
}
if (browser && browser->client()) {
CefRefPtr<CefExtensionHandler> handler = GetCefExtension()->GetHandler();
if (handler) {
return handler->CanAccessBrowser(GetCefExtension(), browser.get(),
function_->include_incognito(), target);
}
}
// Default to allowing access.
return true;
}
CefRefPtr<CefBrowserHostImpl>
CefExtensionFunctionDetails::GetBrowserForTabIdFirstTime(
int tab_id,
std::string* error_message) const {
DCHECK_GE(tab_id, -1);
DCHECK(!get_browser_called_first_time_);
get_browser_called_first_time_ = true;
CefRefPtr<CefBrowserHostImpl> browser;
if (tab_id != -1) {
// May be an invalid tabId or in the wrong BrowserContext.
browser = GetBrowserForTabId(tab_id, function_->browser_context());
if (!browser || !browser->web_contents() || !CanAccessBrowser(browser)) {
if (error_message) {
*error_message = ErrorUtils::FormatErrorMessage(
keys::kTabNotFoundError, base::IntToString(tab_id));
}
return nullptr;
}
} else {
// May return NULL during shutdown.
browser = GetCurrentBrowser();
if (!browser || !browser->web_contents()) {
if (error_message) {
*error_message = keys::kNoCurrentWindowError;
}
return nullptr;
}
}
return browser;
}
CefRefPtr<CefBrowserHostImpl>
CefExtensionFunctionDetails::GetBrowserForTabIdAgain(
int tab_id,
std::string* error_message) const {
DCHECK_GE(tab_id, 0);
DCHECK(get_browser_called_first_time_);
// May return NULL during shutdown.
CefRefPtr<CefBrowserHostImpl> browser =
GetBrowserForTabId(tab_id, function_->browser_context());
if (!browser || !browser->web_contents()) {
if (error_message) {
*error_message = ErrorUtils::FormatErrorMessage(
keys::kTabNotFoundError, base::IntToString(tab_id));
}
}
return browser;
}
bool CefExtensionFunctionDetails::LoadFile(const std::string& file,
LoadFileCallback callback) const {
// Start with the browser hosting the extension.
CefRefPtr<CefBrowserHostImpl> browser = GetSenderBrowser();
if (browser && browser->client()) {
CefRefPtr<CefExtensionHandler> handler = GetCefExtension()->GetHandler();
if (handler) {
CefRefPtr<CefGetExtensionLoadFileCallbackImpl> cef_callback(
new CefGetExtensionLoadFileCallbackImpl(file, std::move(callback)));
if (handler->GetExtensionResource(GetCefExtension(), browser.get(), file,
cef_callback)) {
return true;
}
cef_callback->Disconnect();
}
}
return false;
}
CefRefPtr<CefExtension> CefExtensionFunctionDetails::GetCefExtension() const {
if (!cef_extension_) {
cef_extension_ =
static_cast<CefBrowserContext*>(function_->browser_context())
->extension_system()
->GetExtension(function_->extension_id());
DCHECK(cef_extension_);
}
return cef_extension_;
}
} // namespace extensions