inspectable_web_contents_impl.cc 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Copyright (c) 2013 Adam Roben <adam@roben.org>. All rights reserved.
  3. // Use of this source code is governed by a BSD-style license that can be
  4. // found in the LICENSE-CHROMIUM file.
  5. #include <utility>
  6. #include "brightray/browser/inspectable_web_contents_impl.h"
  7. #include "base/guid.h"
  8. #include "base/json/json_reader.h"
  9. #include "base/json/json_writer.h"
  10. #include "base/metrics/histogram.h"
  11. #include "base/strings/pattern.h"
  12. #include "base/strings/string_util.h"
  13. #include "base/strings/stringprintf.h"
  14. #include "base/strings/utf_string_conversions.h"
  15. #include "base/values.h"
  16. #include "brightray/browser/browser_client.h"
  17. #include "brightray/browser/browser_context.h"
  18. #include "brightray/browser/browser_main_parts.h"
  19. #include "brightray/browser/inspectable_web_contents_delegate.h"
  20. #include "brightray/browser/inspectable_web_contents_view.h"
  21. #include "brightray/browser/inspectable_web_contents_view_delegate.h"
  22. #include "components/prefs/pref_registry_simple.h"
  23. #include "components/prefs/pref_service.h"
  24. #include "components/prefs/scoped_user_pref_update.h"
  25. #include "content/public/browser/browser_thread.h"
  26. #include "content/public/browser/host_zoom_map.h"
  27. #include "content/public/browser/navigation_handle.h"
  28. #include "content/public/browser/render_frame_host.h"
  29. #include "content/public/browser/render_view_host.h"
  30. #include "content/public/common/user_agent.h"
  31. #include "ipc/ipc_channel.h"
  32. #include "net/http/http_response_headers.h"
  33. #include "net/url_request/url_fetcher.h"
  34. #include "net/url_request/url_fetcher_response_writer.h"
  35. #include "ui/display/display.h"
  36. #include "ui/display/screen.h"
  37. namespace brightray {
  38. namespace {
  39. const double kPresetZoomFactors[] = {0.25, 0.333, 0.5, 0.666, 0.75, 0.9,
  40. 1.0, 1.1, 1.25, 1.5, 1.75, 2.0,
  41. 2.5, 3.0, 4.0, 5.0};
  42. const char kChromeUIDevToolsURL[] =
  43. "chrome-devtools://devtools/bundled/inspector.html?"
  44. "remoteBase=%s&"
  45. "can_dock=%s&"
  46. "toolbarColor=rgba(223,223,223,1)&"
  47. "textColor=rgba(0,0,0,1)&"
  48. "experiments=true";
  49. const char kChromeUIDevToolsRemoteFrontendBase[] =
  50. "https://chrome-devtools-frontend.appspot.com/";
  51. const char kChromeUIDevToolsRemoteFrontendPath[] = "serve_file";
  52. const char kDevToolsBoundsPref[] = "brightray.devtools.bounds";
  53. const char kDevToolsZoomPref[] = "brightray.devtools.zoom";
  54. const char kDevToolsPreferences[] = "brightray.devtools.preferences";
  55. const char kFrontendHostId[] = "id";
  56. const char kFrontendHostMethod[] = "method";
  57. const char kFrontendHostParams[] = "params";
  58. const char kTitleFormat[] = "Developer Tools - %s";
  59. const size_t kMaxMessageChunkSize = IPC::Channel::kMaximumMessageSize / 4;
  60. void RectToDictionary(const gfx::Rect& bounds, base::DictionaryValue* dict) {
  61. dict->SetInteger("x", bounds.x());
  62. dict->SetInteger("y", bounds.y());
  63. dict->SetInteger("width", bounds.width());
  64. dict->SetInteger("height", bounds.height());
  65. }
  66. void DictionaryToRect(const base::DictionaryValue& dict, gfx::Rect* bounds) {
  67. int x = 0, y = 0, width = 800, height = 600;
  68. dict.GetInteger("x", &x);
  69. dict.GetInteger("y", &y);
  70. dict.GetInteger("width", &width);
  71. dict.GetInteger("height", &height);
  72. *bounds = gfx::Rect(x, y, width, height);
  73. }
  74. bool IsPointInRect(const gfx::Point& point, const gfx::Rect& rect) {
  75. return point.x() > rect.x() && point.x() < (rect.width() + rect.x()) &&
  76. point.y() > rect.y() && point.y() < (rect.height() + rect.y());
  77. }
  78. bool IsPointInScreen(const gfx::Point& point) {
  79. for (const auto& display : display::Screen::GetScreen()->GetAllDisplays()) {
  80. if (IsPointInRect(point, display.bounds()))
  81. return true;
  82. }
  83. return false;
  84. }
  85. void SetZoomLevelForWebContents(content::WebContents* web_contents,
  86. double level) {
  87. content::HostZoomMap::SetZoomLevel(web_contents, level);
  88. }
  89. double GetNextZoomLevel(double level, bool out) {
  90. double factor = content::ZoomLevelToZoomFactor(level);
  91. size_t size = arraysize(kPresetZoomFactors);
  92. for (size_t i = 0; i < size; ++i) {
  93. if (!content::ZoomValuesEqual(kPresetZoomFactors[i], factor))
  94. continue;
  95. if (out && i > 0)
  96. return content::ZoomFactorToZoomLevel(kPresetZoomFactors[i - 1]);
  97. if (!out && i != size - 1)
  98. return content::ZoomFactorToZoomLevel(kPresetZoomFactors[i + 1]);
  99. }
  100. return level;
  101. }
  102. GURL GetRemoteBaseURL() {
  103. return GURL(base::StringPrintf("%s%s/%s/",
  104. kChromeUIDevToolsRemoteFrontendBase,
  105. kChromeUIDevToolsRemoteFrontendPath,
  106. content::GetWebKitRevision().c_str()));
  107. }
  108. GURL GetDevToolsURL(bool can_dock) {
  109. auto url_string = base::StringPrintf(kChromeUIDevToolsURL,
  110. GetRemoteBaseURL().spec().c_str(),
  111. can_dock ? "true" : "");
  112. return GURL(url_string);
  113. }
  114. // ResponseWriter -------------------------------------------------------------
  115. class ResponseWriter : public net::URLFetcherResponseWriter {
  116. public:
  117. ResponseWriter(base::WeakPtr<InspectableWebContentsImpl> bindings,
  118. int stream_id);
  119. ~ResponseWriter() override;
  120. // URLFetcherResponseWriter overrides:
  121. int Initialize(const net::CompletionCallback& callback) override;
  122. int Write(net::IOBuffer* buffer,
  123. int num_bytes,
  124. const net::CompletionCallback& callback) override;
  125. int Finish(int net_error, const net::CompletionCallback& callback) override;
  126. private:
  127. base::WeakPtr<InspectableWebContentsImpl> bindings_;
  128. int stream_id_;
  129. DISALLOW_COPY_AND_ASSIGN(ResponseWriter);
  130. };
  131. ResponseWriter::ResponseWriter(
  132. base::WeakPtr<InspectableWebContentsImpl> bindings,
  133. int stream_id)
  134. : bindings_(bindings), stream_id_(stream_id) {}
  135. ResponseWriter::~ResponseWriter() {}
  136. int ResponseWriter::Initialize(const net::CompletionCallback& callback) {
  137. return net::OK;
  138. }
  139. int ResponseWriter::Write(net::IOBuffer* buffer,
  140. int num_bytes,
  141. const net::CompletionCallback& callback) {
  142. auto* id = new base::Value(stream_id_);
  143. base::Value* chunk = new base::Value(std::string(buffer->data(), num_bytes));
  144. content::BrowserThread::PostTask(
  145. content::BrowserThread::UI, FROM_HERE,
  146. base::BindOnce(&InspectableWebContentsImpl::CallClientFunction, bindings_,
  147. "DevToolsAPI.streamWrite", base::Owned(id),
  148. base::Owned(chunk), nullptr));
  149. return num_bytes;
  150. }
  151. int ResponseWriter::Finish(int net_error,
  152. const net::CompletionCallback& callback) {
  153. return net::OK;
  154. }
  155. } // namespace
  156. // Implemented separately on each platform.
  157. InspectableWebContentsView* CreateInspectableContentsView(
  158. InspectableWebContentsImpl* inspectable_web_contents_impl);
  159. void InspectableWebContentsImpl::RegisterPrefs(PrefRegistrySimple* registry) {
  160. std::unique_ptr<base::DictionaryValue> bounds_dict(new base::DictionaryValue);
  161. RectToDictionary(gfx::Rect(0, 0, 800, 600), bounds_dict.get());
  162. registry->RegisterDictionaryPref(kDevToolsBoundsPref, std::move(bounds_dict));
  163. registry->RegisterDoublePref(kDevToolsZoomPref, 0.);
  164. registry->RegisterDictionaryPref(kDevToolsPreferences);
  165. }
  166. InspectableWebContentsImpl::InspectableWebContentsImpl(
  167. content::WebContents* web_contents)
  168. : frontend_loaded_(false),
  169. can_dock_(true),
  170. delegate_(nullptr),
  171. web_contents_(web_contents),
  172. weak_factory_(this) {
  173. auto* context =
  174. static_cast<BrowserContext*>(web_contents_->GetBrowserContext());
  175. pref_service_ = context->prefs();
  176. auto* bounds_dict = pref_service_->GetDictionary(kDevToolsBoundsPref);
  177. if (bounds_dict) {
  178. DictionaryToRect(*bounds_dict, &devtools_bounds_);
  179. // Sometimes the devtools window is out of screen or has too small size.
  180. if (devtools_bounds_.height() < 100 || devtools_bounds_.width() < 100) {
  181. devtools_bounds_.set_height(600);
  182. devtools_bounds_.set_width(800);
  183. }
  184. if (!IsPointInScreen(devtools_bounds_.origin())) {
  185. gfx::Rect display;
  186. if (web_contents->GetNativeView()) {
  187. display = display::Screen::GetScreen()
  188. ->GetDisplayNearestView(web_contents->GetNativeView())
  189. .bounds();
  190. } else {
  191. display = display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
  192. }
  193. devtools_bounds_.set_x(display.x() +
  194. (display.width() - devtools_bounds_.width()) / 2);
  195. devtools_bounds_.set_y(
  196. display.y() + (display.height() - devtools_bounds_.height()) / 2);
  197. }
  198. }
  199. view_.reset(CreateInspectableContentsView(this));
  200. }
  201. InspectableWebContentsImpl::~InspectableWebContentsImpl() {
  202. // Unsubscribe from devtools and Clean up resources.
  203. if (GetDevToolsWebContents()) {
  204. if (managed_devtools_web_contents_)
  205. managed_devtools_web_contents_->SetDelegate(nullptr);
  206. // Calling this also unsubscribes the observer, so WebContentsDestroyed
  207. // won't be called again.
  208. WebContentsDestroyed();
  209. }
  210. // Let destructor destroy managed_devtools_web_contents_.
  211. }
  212. InspectableWebContentsView* InspectableWebContentsImpl::GetView() const {
  213. return view_.get();
  214. }
  215. content::WebContents* InspectableWebContentsImpl::GetWebContents() const {
  216. return web_contents_.get();
  217. }
  218. content::WebContents* InspectableWebContentsImpl::GetDevToolsWebContents()
  219. const {
  220. if (external_devtools_web_contents_)
  221. return external_devtools_web_contents_;
  222. else
  223. return managed_devtools_web_contents_.get();
  224. }
  225. void InspectableWebContentsImpl::InspectElement(int x, int y) {
  226. if (agent_host_.get())
  227. agent_host_->InspectElement(this, x, y);
  228. }
  229. void InspectableWebContentsImpl::SetDelegate(
  230. InspectableWebContentsDelegate* delegate) {
  231. delegate_ = delegate;
  232. }
  233. InspectableWebContentsDelegate* InspectableWebContentsImpl::GetDelegate()
  234. const {
  235. return delegate_;
  236. }
  237. void InspectableWebContentsImpl::SetDockState(const std::string& state) {
  238. if (state == "detach") {
  239. can_dock_ = false;
  240. } else {
  241. can_dock_ = true;
  242. dock_state_ = state;
  243. }
  244. }
  245. void InspectableWebContentsImpl::SetDevToolsWebContents(
  246. content::WebContents* devtools) {
  247. if (!managed_devtools_web_contents_)
  248. external_devtools_web_contents_ = devtools;
  249. }
  250. void InspectableWebContentsImpl::ShowDevTools() {
  251. if (embedder_message_dispatcher_) {
  252. if (managed_devtools_web_contents_)
  253. view_->ShowDevTools();
  254. return;
  255. }
  256. // Show devtools only after it has done loading, this is to make sure the
  257. // SetIsDocked is called *BEFORE* ShowDevTools.
  258. embedder_message_dispatcher_.reset(
  259. DevToolsEmbedderMessageDispatcher::CreateForDevToolsFrontend(this));
  260. if (!external_devtools_web_contents_) { // no external devtools
  261. managed_devtools_web_contents_.reset(
  262. content::WebContents::Create(content::WebContents::CreateParams(
  263. web_contents_->GetBrowserContext())));
  264. managed_devtools_web_contents_->SetDelegate(this);
  265. }
  266. Observe(GetDevToolsWebContents());
  267. AttachTo(content::DevToolsAgentHost::GetOrCreateFor(web_contents_.get()));
  268. GetDevToolsWebContents()->GetController().LoadURL(
  269. GetDevToolsURL(can_dock_), content::Referrer(),
  270. ui::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string());
  271. }
  272. void InspectableWebContentsImpl::CloseDevTools() {
  273. if (GetDevToolsWebContents()) {
  274. frontend_loaded_ = false;
  275. if (managed_devtools_web_contents_) {
  276. view_->CloseDevTools();
  277. managed_devtools_web_contents_.reset();
  278. }
  279. embedder_message_dispatcher_.reset();
  280. web_contents_->Focus();
  281. }
  282. }
  283. bool InspectableWebContentsImpl::IsDevToolsViewShowing() {
  284. return managed_devtools_web_contents_ && view_->IsDevToolsViewShowing();
  285. }
  286. void InspectableWebContentsImpl::AttachTo(
  287. scoped_refptr<content::DevToolsAgentHost> host) {
  288. if (agent_host_.get())
  289. Detach();
  290. agent_host_ = std::move(host);
  291. // Terminate existing debugging connections and start debugging.
  292. agent_host_->ForceAttachClient(this);
  293. }
  294. void InspectableWebContentsImpl::Detach() {
  295. if (agent_host_.get())
  296. agent_host_->DetachClient(this);
  297. agent_host_ = nullptr;
  298. }
  299. void InspectableWebContentsImpl::CallClientFunction(
  300. const std::string& function_name,
  301. const base::Value* arg1,
  302. const base::Value* arg2,
  303. const base::Value* arg3) {
  304. if (!GetDevToolsWebContents())
  305. return;
  306. std::string javascript = function_name + "(";
  307. if (arg1) {
  308. std::string json;
  309. base::JSONWriter::Write(*arg1, &json);
  310. javascript.append(json);
  311. if (arg2) {
  312. base::JSONWriter::Write(*arg2, &json);
  313. javascript.append(", ").append(json);
  314. if (arg3) {
  315. base::JSONWriter::Write(*arg3, &json);
  316. javascript.append(", ").append(json);
  317. }
  318. }
  319. }
  320. javascript.append(");");
  321. GetDevToolsWebContents()->GetMainFrame()->ExecuteJavaScript(
  322. base::UTF8ToUTF16(javascript));
  323. }
  324. gfx::Rect InspectableWebContentsImpl::GetDevToolsBounds() const {
  325. return devtools_bounds_;
  326. }
  327. void InspectableWebContentsImpl::SaveDevToolsBounds(const gfx::Rect& bounds) {
  328. base::DictionaryValue bounds_dict;
  329. RectToDictionary(bounds, &bounds_dict);
  330. pref_service_->Set(kDevToolsBoundsPref, bounds_dict);
  331. devtools_bounds_ = bounds;
  332. }
  333. double InspectableWebContentsImpl::GetDevToolsZoomLevel() const {
  334. return pref_service_->GetDouble(kDevToolsZoomPref);
  335. }
  336. void InspectableWebContentsImpl::UpdateDevToolsZoomLevel(double level) {
  337. pref_service_->SetDouble(kDevToolsZoomPref, level);
  338. }
  339. void InspectableWebContentsImpl::ActivateWindow() {
  340. // Set the zoom level.
  341. SetZoomLevelForWebContents(GetDevToolsWebContents(), GetDevToolsZoomLevel());
  342. }
  343. void InspectableWebContentsImpl::CloseWindow() {
  344. GetDevToolsWebContents()->DispatchBeforeUnload();
  345. }
  346. void InspectableWebContentsImpl::LoadCompleted() {
  347. frontend_loaded_ = true;
  348. if (managed_devtools_web_contents_)
  349. view_->ShowDevTools();
  350. // If the devtools can dock, "SetIsDocked" will be called by devtools itself.
  351. if (!can_dock_) {
  352. SetIsDocked(DispatchCallback(), false);
  353. } else {
  354. if (dock_state_.empty()) {
  355. const base::DictionaryValue* prefs =
  356. pref_service_->GetDictionary(kDevToolsPreferences);
  357. std::string current_dock_state;
  358. prefs->GetString("currentDockState", &current_dock_state);
  359. base::RemoveChars(current_dock_state, "\"", &dock_state_);
  360. }
  361. base::string16 javascript = base::UTF8ToUTF16(
  362. "Components.dockController.setDockSide(\"" + dock_state_ + "\");");
  363. GetDevToolsWebContents()->GetMainFrame()->ExecuteJavaScript(javascript);
  364. }
  365. if (view_->GetDelegate())
  366. view_->GetDelegate()->DevToolsOpened();
  367. }
  368. void InspectableWebContentsImpl::SetInspectedPageBounds(const gfx::Rect& rect) {
  369. DevToolsContentsResizingStrategy strategy(rect);
  370. if (contents_resizing_strategy_.Equals(strategy))
  371. return;
  372. contents_resizing_strategy_.CopyFrom(strategy);
  373. if (managed_devtools_web_contents_)
  374. view_->SetContentsResizingStrategy(contents_resizing_strategy_);
  375. }
  376. void InspectableWebContentsImpl::InspectElementCompleted() {}
  377. void InspectableWebContentsImpl::InspectedURLChanged(const std::string& url) {
  378. if (managed_devtools_web_contents_)
  379. view_->SetTitle(
  380. base::UTF8ToUTF16(base::StringPrintf(kTitleFormat, url.c_str())));
  381. }
  382. void InspectableWebContentsImpl::LoadNetworkResource(
  383. const DispatchCallback& callback,
  384. const std::string& url,
  385. const std::string& headers,
  386. int stream_id) {
  387. GURL gurl(url);
  388. if (!gurl.is_valid()) {
  389. base::DictionaryValue response;
  390. response.SetInteger("statusCode", 404);
  391. callback.Run(&response);
  392. return;
  393. }
  394. auto* browser_context = static_cast<BrowserContext*>(
  395. GetDevToolsWebContents()->GetBrowserContext());
  396. net::URLFetcher* fetcher =
  397. (net::URLFetcher::Create(gurl, net::URLFetcher::GET, this)).release();
  398. pending_requests_[fetcher] = callback;
  399. fetcher->SetRequestContext(browser_context->url_request_context_getter());
  400. fetcher->SetExtraRequestHeaders(headers);
  401. fetcher->SaveResponseWithWriter(
  402. std::unique_ptr<net::URLFetcherResponseWriter>(
  403. new ResponseWriter(weak_factory_.GetWeakPtr(), stream_id)));
  404. fetcher->Start();
  405. }
  406. void InspectableWebContentsImpl::SetIsDocked(const DispatchCallback& callback,
  407. bool docked) {
  408. if (managed_devtools_web_contents_)
  409. view_->SetIsDocked(docked);
  410. if (!callback.is_null())
  411. callback.Run(nullptr);
  412. }
  413. void InspectableWebContentsImpl::OpenInNewTab(const std::string& url) {}
  414. void InspectableWebContentsImpl::SaveToFile(const std::string& url,
  415. const std::string& content,
  416. bool save_as) {
  417. if (delegate_)
  418. delegate_->DevToolsSaveToFile(url, content, save_as);
  419. }
  420. void InspectableWebContentsImpl::AppendToFile(const std::string& url,
  421. const std::string& content) {
  422. if (delegate_)
  423. delegate_->DevToolsAppendToFile(url, content);
  424. }
  425. void InspectableWebContentsImpl::RequestFileSystems() {
  426. if (delegate_)
  427. delegate_->DevToolsRequestFileSystems();
  428. }
  429. void InspectableWebContentsImpl::AddFileSystem(
  430. const std::string& file_system_path) {
  431. if (delegate_)
  432. delegate_->DevToolsAddFileSystem(
  433. base::FilePath::FromUTF8Unsafe(file_system_path));
  434. }
  435. void InspectableWebContentsImpl::RemoveFileSystem(
  436. const std::string& file_system_path) {
  437. if (delegate_)
  438. delegate_->DevToolsRemoveFileSystem(
  439. base::FilePath::FromUTF8Unsafe(file_system_path));
  440. }
  441. void InspectableWebContentsImpl::UpgradeDraggedFileSystemPermissions(
  442. const std::string& file_system_url) {}
  443. void InspectableWebContentsImpl::IndexPath(
  444. int request_id,
  445. const std::string& file_system_path) {
  446. if (delegate_)
  447. delegate_->DevToolsIndexPath(request_id, file_system_path);
  448. }
  449. void InspectableWebContentsImpl::StopIndexing(int request_id) {
  450. if (delegate_)
  451. delegate_->DevToolsStopIndexing(request_id);
  452. }
  453. void InspectableWebContentsImpl::SearchInPath(
  454. int request_id,
  455. const std::string& file_system_path,
  456. const std::string& query) {
  457. if (delegate_)
  458. delegate_->DevToolsSearchInPath(request_id, file_system_path, query);
  459. }
  460. void InspectableWebContentsImpl::SetWhitelistedShortcuts(
  461. const std::string& message) {}
  462. void InspectableWebContentsImpl::ZoomIn() {
  463. double new_level = GetNextZoomLevel(GetDevToolsZoomLevel(), false);
  464. SetZoomLevelForWebContents(GetDevToolsWebContents(), new_level);
  465. UpdateDevToolsZoomLevel(new_level);
  466. }
  467. void InspectableWebContentsImpl::ZoomOut() {
  468. double new_level = GetNextZoomLevel(GetDevToolsZoomLevel(), true);
  469. SetZoomLevelForWebContents(GetDevToolsWebContents(), new_level);
  470. UpdateDevToolsZoomLevel(new_level);
  471. }
  472. void InspectableWebContentsImpl::ResetZoom() {
  473. SetZoomLevelForWebContents(GetDevToolsWebContents(), 0.);
  474. UpdateDevToolsZoomLevel(0.);
  475. }
  476. void InspectableWebContentsImpl::SetDevicesUpdatesEnabled(bool enabled) {}
  477. void InspectableWebContentsImpl::DispatchProtocolMessageFromDevToolsFrontend(
  478. const std::string& message) {
  479. // If the devtools wants to reload the page, hijack the message and handle it
  480. // to the delegate.
  481. if (base::MatchPattern(message,
  482. "{\"id\":*,"
  483. "\"method\":\"Page.reload\","
  484. "\"params\":*}")) {
  485. if (delegate_)
  486. delegate_->DevToolsReloadPage();
  487. return;
  488. }
  489. if (agent_host_.get())
  490. agent_host_->DispatchProtocolMessage(this, message);
  491. }
  492. void InspectableWebContentsImpl::SendJsonRequest(
  493. const DispatchCallback& callback,
  494. const std::string& browser_id,
  495. const std::string& url) {
  496. callback.Run(nullptr);
  497. }
  498. void InspectableWebContentsImpl::GetPreferences(
  499. const DispatchCallback& callback) {
  500. const base::DictionaryValue* prefs =
  501. pref_service_->GetDictionary(kDevToolsPreferences);
  502. callback.Run(prefs);
  503. }
  504. void InspectableWebContentsImpl::SetPreference(const std::string& name,
  505. const std::string& value) {
  506. DictionaryPrefUpdate update(pref_service_, kDevToolsPreferences);
  507. update.Get()->SetKey(name, base::Value(value));
  508. }
  509. void InspectableWebContentsImpl::RemovePreference(const std::string& name) {
  510. DictionaryPrefUpdate update(pref_service_, kDevToolsPreferences);
  511. update.Get()->RemoveWithoutPathExpansion(name, nullptr);
  512. }
  513. void InspectableWebContentsImpl::ClearPreferences() {
  514. DictionaryPrefUpdate update(pref_service_, kDevToolsPreferences);
  515. update.Get()->Clear();
  516. }
  517. void InspectableWebContentsImpl::RegisterExtensionsAPI(
  518. const std::string& origin,
  519. const std::string& script) {
  520. extensions_api_[origin + "/"] = script;
  521. }
  522. void InspectableWebContentsImpl::HandleMessageFromDevToolsFrontend(
  523. const std::string& message) {
  524. std::string method;
  525. base::ListValue empty_params;
  526. base::ListValue* params = &empty_params;
  527. base::DictionaryValue* dict = nullptr;
  528. std::unique_ptr<base::Value> parsed_message(base::JSONReader::Read(message));
  529. if (!parsed_message || !parsed_message->GetAsDictionary(&dict) ||
  530. !dict->GetString(kFrontendHostMethod, &method) ||
  531. (dict->HasKey(kFrontendHostParams) &&
  532. !dict->GetList(kFrontendHostParams, &params))) {
  533. LOG(ERROR) << "Invalid message was sent to embedder: " << message;
  534. return;
  535. }
  536. int id = 0;
  537. dict->GetInteger(kFrontendHostId, &id);
  538. embedder_message_dispatcher_->Dispatch(
  539. base::Bind(&InspectableWebContentsImpl::SendMessageAck,
  540. weak_factory_.GetWeakPtr(), id),
  541. method, params);
  542. }
  543. void InspectableWebContentsImpl::DispatchProtocolMessage(
  544. content::DevToolsAgentHost* agent_host,
  545. const std::string& message) {
  546. if (!frontend_loaded_)
  547. return;
  548. if (message.length() < kMaxMessageChunkSize) {
  549. base::string16 javascript =
  550. base::UTF8ToUTF16("DevToolsAPI.dispatchMessage(" + message + ");");
  551. GetDevToolsWebContents()->GetMainFrame()->ExecuteJavaScript(javascript);
  552. return;
  553. }
  554. base::Value total_size(static_cast<int>(message.length()));
  555. for (size_t pos = 0; pos < message.length(); pos += kMaxMessageChunkSize) {
  556. base::Value message_value(message.substr(pos, kMaxMessageChunkSize));
  557. CallClientFunction("DevToolsAPI.dispatchMessageChunk", &message_value,
  558. pos ? nullptr : &total_size, nullptr);
  559. }
  560. }
  561. void InspectableWebContentsImpl::AgentHostClosed(
  562. content::DevToolsAgentHost* agent_host,
  563. bool replaced) {}
  564. void InspectableWebContentsImpl::RenderFrameHostChanged(
  565. content::RenderFrameHost* old_host,
  566. content::RenderFrameHost* new_host) {
  567. if (new_host->GetParent())
  568. return;
  569. frontend_host_.reset(content::DevToolsFrontendHost::Create(
  570. new_host,
  571. base::Bind(&InspectableWebContentsImpl::HandleMessageFromDevToolsFrontend,
  572. weak_factory_.GetWeakPtr())));
  573. }
  574. void InspectableWebContentsImpl::WebContentsDestroyed() {
  575. frontend_loaded_ = false;
  576. external_devtools_web_contents_ = nullptr;
  577. Observe(nullptr);
  578. Detach();
  579. embedder_message_dispatcher_.reset();
  580. for (const auto& pair : pending_requests_)
  581. delete pair.first;
  582. if (view_ && view_->GetDelegate())
  583. view_->GetDelegate()->DevToolsClosed();
  584. }
  585. bool InspectableWebContentsImpl::DidAddMessageToConsole(
  586. content::WebContents* source,
  587. int32_t level,
  588. const base::string16& message,
  589. int32_t line_no,
  590. const base::string16& source_id) {
  591. logging::LogMessage("CONSOLE", line_no, level).stream()
  592. << "\"" << message << "\", source: " << source_id << " (" << line_no
  593. << ")";
  594. return true;
  595. }
  596. bool InspectableWebContentsImpl::ShouldCreateWebContents(
  597. content::WebContents* web_contents,
  598. content::RenderFrameHost* opener,
  599. content::SiteInstance* source_site_instance,
  600. int32_t route_id,
  601. int32_t main_frame_route_id,
  602. int32_t main_frame_widget_route_id,
  603. content::mojom::WindowContainerType window_container_type,
  604. const GURL& opener_url,
  605. const std::string& frame_name,
  606. const GURL& target_url,
  607. const std::string& partition_id,
  608. content::SessionStorageNamespace* session_storage_namespace) {
  609. return false;
  610. }
  611. void InspectableWebContentsImpl::HandleKeyboardEvent(
  612. content::WebContents* source,
  613. const content::NativeWebKeyboardEvent& event) {
  614. auto* delegate = web_contents_->GetDelegate();
  615. if (delegate)
  616. delegate->HandleKeyboardEvent(source, event);
  617. }
  618. void InspectableWebContentsImpl::CloseContents(content::WebContents* source) {
  619. // This is where the devtools closes itself (by clicking the x button).
  620. CloseDevTools();
  621. }
  622. content::ColorChooser* InspectableWebContentsImpl::OpenColorChooser(
  623. content::WebContents* source,
  624. SkColor color,
  625. const std::vector<content::ColorSuggestion>& suggestions) {
  626. auto* delegate = web_contents_->GetDelegate();
  627. if (delegate)
  628. return delegate->OpenColorChooser(source, color, suggestions);
  629. return nullptr;
  630. }
  631. void InspectableWebContentsImpl::RunFileChooser(
  632. content::RenderFrameHost* render_frame_host,
  633. const content::FileChooserParams& params) {
  634. auto* delegate = web_contents_->GetDelegate();
  635. if (delegate)
  636. delegate->RunFileChooser(render_frame_host, params);
  637. }
  638. void InspectableWebContentsImpl::EnumerateDirectory(
  639. content::WebContents* source,
  640. int request_id,
  641. const base::FilePath& path) {
  642. auto* delegate = web_contents_->GetDelegate();
  643. if (delegate)
  644. delegate->EnumerateDirectory(source, request_id, path);
  645. }
  646. void InspectableWebContentsImpl::OnWebContentsFocused(
  647. content::RenderWidgetHost* render_widget_host) {
  648. #if defined(TOOLKIT_VIEWS)
  649. if (view_->GetDelegate())
  650. view_->GetDelegate()->DevToolsFocused();
  651. #endif
  652. }
  653. void InspectableWebContentsImpl::ReadyToCommitNavigation(
  654. content::NavigationHandle* navigation_handle) {
  655. if (navigation_handle->IsInMainFrame()) {
  656. if (navigation_handle->GetRenderFrameHost() ==
  657. GetDevToolsWebContents()->GetMainFrame() &&
  658. frontend_host_) {
  659. return;
  660. }
  661. frontend_host_.reset(content::DevToolsFrontendHost::Create(
  662. web_contents()->GetMainFrame(),
  663. base::Bind(
  664. &InspectableWebContentsImpl::HandleMessageFromDevToolsFrontend,
  665. base::Unretained(this))));
  666. return;
  667. }
  668. }
  669. void InspectableWebContentsImpl::DidFinishNavigation(
  670. content::NavigationHandle* navigation_handle) {
  671. if (navigation_handle->IsInMainFrame() ||
  672. !navigation_handle->GetURL().SchemeIs("chrome-extension") ||
  673. !navigation_handle->HasCommitted())
  674. return;
  675. content::RenderFrameHost* frame = navigation_handle->GetRenderFrameHost();
  676. auto origin = navigation_handle->GetURL().GetOrigin().spec();
  677. auto it = extensions_api_.find(origin);
  678. if (it == extensions_api_.end())
  679. return;
  680. // Injected Script from devtools frontend doesn't expose chrome,
  681. // most likely bug in chromium.
  682. base::ReplaceFirstSubstringAfterOffset(&it->second, 0, "var chrome",
  683. "var chrome = window.chrome ");
  684. auto script = base::StringPrintf("%s(\"%s\")", it->second.c_str(),
  685. base::GenerateGUID().c_str());
  686. // Invoking content::DevToolsFrontendHost::SetupExtensionsAPI(frame, script);
  687. // should be enough, but it seems to be a noop currently.
  688. frame->ExecuteJavaScriptForTests(base::UTF8ToUTF16(script));
  689. }
  690. void InspectableWebContentsImpl::OnURLFetchComplete(
  691. const net::URLFetcher* source) {
  692. DCHECK(source);
  693. auto it = pending_requests_.find(source);
  694. DCHECK(it != pending_requests_.end());
  695. base::DictionaryValue response;
  696. net::HttpResponseHeaders* rh = source->GetResponseHeaders();
  697. response.SetInteger("statusCode", rh ? rh->response_code() : 200);
  698. {
  699. auto headers = std::make_unique<base::DictionaryValue>();
  700. size_t iterator = 0;
  701. std::string name;
  702. std::string value;
  703. while (rh && rh->EnumerateHeaderLines(&iterator, &name, &value))
  704. headers->SetString(name, value);
  705. response.Set("headers", std::move(headers));
  706. }
  707. it->second.Run(&response);
  708. pending_requests_.erase(it);
  709. delete source;
  710. }
  711. void InspectableWebContentsImpl::SendMessageAck(int request_id,
  712. const base::Value* arg) {
  713. base::Value id_value(request_id);
  714. CallClientFunction("DevToolsAPI.embedderMessageAck", &id_value, arg, nullptr);
  715. }
  716. } // namespace brightray