remote/browser/RemoteBrowser.cpp (206 lines of code) (raw):
#include "RemoteBrowser.h"
#include "RemoteClient.h"
#include "../handlers/RemoteLifespanHandler.h"
#include "../router/MessageRoutersManager.h"
#include "../handlers/RemoteClientHandler.h"
#include "include/base/cef_callback.h"
#include "include/cef_app.h"
#include "include/cef_task.h"
#include "include/wrapper/cef_closure_task.h"
#include "include/cef_browser.h"
std::mutex RemoteBrowser::ourBid2BrowserMutex;
std::map<int, std::shared_ptr<RemoteBrowser>> RemoteBrowser::ourBid2Browser;
std::mutex RemoteBrowser::ourCef2RemoteMutex;
std::map<int, std::shared_ptr<RemoteBrowser>> RemoteBrowser::ourCef2Remote;
RemoteBrowser::RemoteBrowser(int bid, std::shared_ptr<RemoteClient> owner,
CefRefPtr<CefRequestContext> requestContext) : myBid(bid), myOwner(owner),
myRequestContext(requestContext) {
}
std::shared_ptr<RemoteBrowser> RemoteBrowser::create(std::shared_ptr<RemoteClient> owner, CefRefPtr<CefRequestContext> requestContext) {
std::shared_ptr<RemoteBrowser> result;
{
std::unique_lock lock(ourBid2BrowserMutex);
static int sBid = 0;
int bid = sBid++;
ourBid2Browser[bid] = result = std::make_shared<RemoteBrowser>(bid, owner, requestContext);
}
return result;
}
int RemoteBrowser::getCid() const { return myOwner->getCid(); }
CefRefPtr<CefRequestContext> RemoteBrowser::getRequestContext() const { return myRequestContext; }
CefRefPtr<CefBrowser> RemoteBrowser::getCefBrowser() const { return myCefBrowser; }
void RemoteBrowser::setCefBrowser(CefRefPtr<CefBrowser> browser) {
myCefBrowser = browser;
}
void RemoteBrowser::linkCefBrowser(CefRefPtr<CefBrowser> browser, std::shared_ptr<RemoteBrowser> remoteBrowser) {
std::unique_lock lock(ourCef2RemoteMutex);
ourCef2Remote[browser->GetIdentifier()] = remoteBrowser;
}
void RemoteBrowser::unlinkCefBrowser(CefRefPtr<CefBrowser> browser) {
std::unique_lock lock(ourCef2RemoteMutex);
ourCef2Remote.erase(browser->GetIdentifier());
}
std::shared_ptr<RemoteBrowser> RemoteBrowser::findByCefBrowser(CefRefPtr<CefBrowser> browser) {
if (!browser)
return nullptr;
std::unique_lock lock(ourCef2RemoteMutex);
auto i = ourCef2Remote.find(browser->GetIdentifier());
return i == ourCef2Remote.end() ? nullptr : i->second;
}
namespace {
CefRefPtr<CefListValue> GetAllMessageRouterConfigs();
// Should be called on UI thread
void createCefBrowserImpl(
int cid, int bid,
CefRefPtr<RemoteClientHandler> clienthandler,
CefRefPtr<CefRequestContext> requestContext,
std::string url,
std::function<void(int)> onCreationFailed
) {
CefBrowserSettings settings; // TODO: get real CefBrowserSettings from java
CefWindowInfo windowInfo;
windowInfo.SetAsWindowless(0);
// JCEF requires Alloy runtime style for "normal" browsers in order for them
// to be integratable into Java UI.
windowInfo.runtime_style = CEF_RUNTIME_STYLE_ALLOY;
CefRefPtr<CefDictionaryValue> extra_info;
auto router_configs = GetAllMessageRouterConfigs();
if (router_configs) {
// Send the message router config to CefHelperApp::OnBrowserCreated.
extra_info = CefDictionaryValue::Create();
extra_info->SetList("router_configs", router_configs);
}
RemoteLifespanHandler* lsh = (RemoteLifespanHandler*)clienthandler->GetLifeSpanHandler().get();
lsh->addCreating(bid);
//Log::trace( "CefBrowserHost::CreateBrowser cid=%d, bid=%d", cid, bid);
bool result = CefBrowserHost::CreateBrowser(windowInfo, clienthandler, url,
settings, extra_info, requestContext);
if (!result) {
Log::error( "Failed to create browser with cid=%d, bid=%d", cid, bid);
lsh->removeCreating(bid);
onCreationFailed(bid);
}
}
// Should be called on UI thread
void openDevToolsPopupImpl(
int bid,
CefRefPtr<CefBrowser> parentBrowser,
CefPoint inspectAt
) {
if (!parentBrowser)
return;
Log::trace( "ShowDevTools: bid=%d, pt=(%d,%d)", bid, inspectAt.x, inspectAt.y);
CefWindowInfo windowInfo;
CefBrowserSettings settings; // TODO: get real CefBrowserSettings from java
parentBrowser->GetHost()->ShowDevTools(windowInfo, nullptr, settings, inspectAt);
}
}
void RemoteBrowser::startNativeBrowserCreation(const std::string & url) {
std::function remove = [=](int bid){
myOwner->eraseBrowser(bid);
std::unique_lock lock(ourBid2BrowserMutex);
ourBid2Browser.erase(bid);
};
if (CefCurrentlyOn(TID_UI)) {
createCefBrowserImpl(getCid(), myBid, myOwner->myRemoteClientHandler, myRequestContext, url, remove);
} else {
CefPostTask(TID_UI, base::BindOnce(&createCefBrowserImpl, getCid(), myBid, myOwner->myRemoteClientHandler, myRequestContext, url, remove));
}
}
void RemoteBrowser::openDevTools(int x, int y) {
if (!myCefBrowser) {
Log::error("Can't open dev-tools for bid=%d, because native CefBrowser wasn't created yet.", myBid);
return;
}
if (CefCurrentlyOn(TID_UI)) {
openDevToolsPopupImpl(myBid, myCefBrowser, CefPoint(x, y));
} else {
CefPostTask(TID_UI, base::BindOnce(&openDevToolsPopupImpl, myBid, myCefBrowser, CefPoint(x, y)));
}
}
void RemoteBrowser::close() {
if (myIsClosing)
return;
//Log::trace("Scheduled closing native browser, bid=%d", myBid);
myIsClosing = true;
if (myCefBrowser != nullptr)
myCefBrowser->GetHost()->CloseBrowser(true);
}
std::shared_ptr<RemoteBrowser> RemoteBrowser::find(int bid) {
std::unique_lock lock(ourBid2BrowserMutex);
const auto & i = ourBid2Browser.find(bid);
return i == ourBid2Browser.end() ? nullptr : i->second;
}
std::vector<int> RemoteBrowser::enumAllBrowsers() {
std::unique_lock lock(ourBid2BrowserMutex);
std::vector<int> result;
for (auto const& rc : ourBid2Browser) {
std::shared_ptr<RemoteBrowser> b = rc.second;
if (b)
result.push_back(b->getBid());
}
return result;
}
unsigned int RemoteBrowser::getAllBrowsersCount() {
std::unique_lock lock(ourBid2BrowserMutex);
return (unsigned int)ourBid2Browser.size();
}
bool RemoteBrowser::closeAllBrowsers() {
bool isEmpty = true;
std::unique_lock lock(ourBid2BrowserMutex);
for (auto const& rc : ourBid2Browser) {
std::shared_ptr<RemoteBrowser> b = rc.second;
if (b) {
b->close();
isEmpty = false;
}
}
return isEmpty;
}
void RemoteBrowser::onBeforeClose() {
myCefBrowser = nullptr;
myOwner->eraseBrowser(myBid);
// Remove the link from a static map
std::unique_lock lock(ourBid2BrowserMutex);
ourBid2Browser.erase(myBid);
}
std::string RemoteBrowser::getDebugInfo(int tabs) {
std::stringstream ss;
for (int i = 0; i < tabs; ++i) ss << "\t";
if (myIsClosing)
ss << "Closing, bid=" << myBid;
else
ss << "Active, bid=" << myBid;
if (myRequestContext)
ss << ", RequestContext=" << myRequestContext.get();
ss << std::endl;
return ss.str();
}
namespace {
// comparator to check if configuration values are the same
struct cmpCfg {
bool operator()(const CefMessageRouterConfig& lValue,
const CefMessageRouterConfig& rValue) const {
std::less<std::string> comp;
return comp(lValue.js_query_function.ToString(),
rValue.js_query_function.ToString());
}
};
std::set<CefMessageRouterConfig, cmpCfg> router_cfg_;
base::Lock router_cfg_lock_;
CefRefPtr<CefListValue> GetAllMessageRouterConfigs() {
int idx = 0;
static std::set<CefMessageRouterConfig, cmpCfg>::iterator iter;
base::AutoLock lock_scope(router_cfg_lock_);
if (router_cfg_.empty())
return nullptr;
// Configuration pased to CefHelperApp::OnBrowserCreated.
auto router_configs = CefListValue::Create();
for (iter = router_cfg_.begin(); iter != router_cfg_.end(); ++iter) {
CefRefPtr<CefDictionaryValue> dict = CefDictionaryValue::Create();
dict->SetString("js_query_function", iter->js_query_function);
dict->SetString("js_cancel_function", iter->js_cancel_function);
router_configs->SetDictionary(idx, dict);
idx++;
}
return router_configs;
}
}
// static
void RemoteBrowser::AddMessageRouterConfig(const CefMessageRouterConfig& cfg) {
base::AutoLock lock_scope(router_cfg_lock_);
router_cfg_.insert(cfg);
}
// static
void RemoteBrowser::RemoveMessageRouterConfig(const CefMessageRouterConfig& cfg) {
base::AutoLock lock_scope(router_cfg_lock_);
router_cfg_.erase(cfg);
}