atom_browser_client.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. // Copyright (c) 2013 GitHub, Inc.
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. #include "atom/browser/atom_browser_client.h"
  5. #if defined(OS_WIN)
  6. #include <shlobj.h>
  7. #endif
  8. #include "atom/browser/api/atom_api_app.h"
  9. #include "atom/browser/api/atom_api_protocol.h"
  10. #include "atom/browser/api/atom_api_web_contents.h"
  11. #include "atom/browser/atom_browser_context.h"
  12. #include "atom/browser/atom_browser_main_parts.h"
  13. #include "atom/browser/atom_quota_permission_context.h"
  14. #include "atom/browser/atom_resource_dispatcher_host_delegate.h"
  15. #include "atom/browser/atom_speech_recognition_manager_delegate.h"
  16. #include "atom/browser/child_web_contents_tracker.h"
  17. #include "atom/browser/native_window.h"
  18. #include "atom/browser/session_preferences.h"
  19. #include "atom/browser/web_contents_permission_helper.h"
  20. #include "atom/browser/web_contents_preferences.h"
  21. #include "atom/browser/window_list.h"
  22. #include "atom/common/options_switches.h"
  23. #include "base/command_line.h"
  24. #include "base/files/file_util.h"
  25. #include "base/stl_util.h"
  26. #include "base/strings/string_number_conversions.h"
  27. #include "base/strings/string_util.h"
  28. #include "chrome/browser/printing/printing_message_filter.h"
  29. #include "chrome/browser/renderer_host/pepper/chrome_browser_pepper_host_factory.h"
  30. #include "chrome/browser/renderer_host/pepper/widevine_cdm_message_filter.h"
  31. #include "chrome/browser/speech/tts_message_filter.h"
  32. #include "content/public/browser/browser_ppapi_host.h"
  33. #include "content/public/browser/client_certificate_delegate.h"
  34. #include "content/public/browser/render_process_host.h"
  35. #include "content/public/browser/render_view_host.h"
  36. #include "content/public/browser/resource_dispatcher_host.h"
  37. #include "content/public/browser/site_instance.h"
  38. #include "content/public/browser/web_contents.h"
  39. #include "content/public/common/content_paths.h"
  40. #include "content/public/common/content_switches.h"
  41. #include "content/public/common/resource_request_body.h"
  42. #include "content/public/common/url_constants.h"
  43. #include "content/public/common/web_preferences.h"
  44. #include "net/ssl/ssl_cert_request_info.h"
  45. #include "ppapi/host/ppapi_host.h"
  46. #include "ui/base/l10n/l10n_util.h"
  47. #include "v8/include/v8.h"
  48. namespace atom {
  49. namespace {
  50. // Next navigation should not restart renderer process.
  51. bool g_suppress_renderer_process_restart = false;
  52. // Custom schemes to be registered to handle service worker.
  53. std::string g_custom_service_worker_schemes = "";
  54. void Noop(scoped_refptr<content::SiteInstance>) {
  55. }
  56. } // namespace
  57. // static
  58. void AtomBrowserClient::SuppressRendererProcessRestartForOnce() {
  59. g_suppress_renderer_process_restart = true;
  60. }
  61. void AtomBrowserClient::SetCustomServiceWorkerSchemes(
  62. const std::vector<std::string>& schemes) {
  63. g_custom_service_worker_schemes = base::JoinString(schemes, ",");
  64. }
  65. AtomBrowserClient::AtomBrowserClient() : delegate_(nullptr) {
  66. }
  67. AtomBrowserClient::~AtomBrowserClient() {
  68. }
  69. content::WebContents* AtomBrowserClient::GetWebContentsFromProcessID(
  70. int process_id) {
  71. // If the process is a pending process, we should use the web contents
  72. // for the frame host passed into OverrideSiteInstanceForNavigation.
  73. if (base::ContainsKey(pending_processes_, process_id))
  74. return pending_processes_[process_id];
  75. // Certain render process will be created with no associated render view,
  76. // for example: ServiceWorker.
  77. return WebContentsPreferences::GetWebContentsFromProcessID(process_id);
  78. }
  79. bool AtomBrowserClient::ShouldCreateNewSiteInstance(
  80. content::RenderFrameHost* render_frame_host,
  81. content::BrowserContext* browser_context,
  82. content::SiteInstance* current_instance,
  83. const GURL& url) {
  84. if (url.SchemeIs(url::kJavaScriptScheme))
  85. // "javacript:" scheme should always use same SiteInstance
  86. return false;
  87. int process_id = current_instance->GetProcess()->GetID();
  88. if (!IsRendererSandboxed(process_id)) {
  89. if (!RendererUsesNativeWindowOpen(process_id)) {
  90. // non-sandboxed renderers without native window.open should always create
  91. // a new SiteInstance
  92. return true;
  93. }
  94. auto web_contents =
  95. content::WebContents::FromRenderFrameHost(render_frame_host);
  96. if (!ChildWebContentsTracker::IsChildWebContents(web_contents)) {
  97. // Root WebContents should always create new process to make sure
  98. // native addons are loaded correctly after reload / navigation.
  99. // (Non-root WebContents opened by window.open() should try to
  100. // reuse process to allow synchronous cross-window scripting.)
  101. return true;
  102. }
  103. }
  104. // Create new a SiteInstance if navigating to a different site.
  105. auto src_url = current_instance->GetSiteURL();
  106. return
  107. !content::SiteInstance::IsSameWebSite(browser_context, src_url, url) &&
  108. // `IsSameWebSite` doesn't seem to work for some URIs such as `file:`,
  109. // handle these scenarios by comparing only the site as defined by
  110. // `GetSiteForURL`.
  111. content::SiteInstance::GetSiteForURL(browser_context, url) != src_url;
  112. }
  113. void AtomBrowserClient::AddProcessPreferences(
  114. int process_id, AtomBrowserClient::ProcessPreferences prefs) {
  115. base::AutoLock auto_lock(process_preferences_lock_);
  116. process_preferences_[process_id] = prefs;
  117. }
  118. void AtomBrowserClient::RemoveProcessPreferences(int process_id) {
  119. base::AutoLock auto_lock(process_preferences_lock_);
  120. process_preferences_.erase(process_id);
  121. }
  122. bool AtomBrowserClient::IsRendererSandboxed(int process_id) {
  123. base::AutoLock auto_lock(process_preferences_lock_);
  124. auto it = process_preferences_.find(process_id);
  125. return it != process_preferences_.end() && it->second.sandbox;
  126. }
  127. bool AtomBrowserClient::RendererUsesNativeWindowOpen(int process_id) {
  128. base::AutoLock auto_lock(process_preferences_lock_);
  129. auto it = process_preferences_.find(process_id);
  130. return it != process_preferences_.end() && it->second.native_window_open;
  131. }
  132. bool AtomBrowserClient::RendererDisablesPopups(int process_id) {
  133. base::AutoLock auto_lock(process_preferences_lock_);
  134. auto it = process_preferences_.find(process_id);
  135. return it != process_preferences_.end() && it->second.disable_popups;
  136. }
  137. void AtomBrowserClient::RenderProcessWillLaunch(
  138. content::RenderProcessHost* host) {
  139. int process_id = host->GetID();
  140. host->AddFilter(new printing::PrintingMessageFilter(process_id));
  141. host->AddFilter(new TtsMessageFilter(process_id, host->GetBrowserContext()));
  142. host->AddFilter(
  143. new WidevineCdmMessageFilter(process_id, host->GetBrowserContext()));
  144. content::WebContents* web_contents = GetWebContentsFromProcessID(process_id);
  145. ProcessPreferences process_prefs;
  146. process_prefs.sandbox =
  147. WebContentsPreferences::IsPreferenceEnabled("sandbox", web_contents);
  148. process_prefs.native_window_open =
  149. WebContentsPreferences::IsPreferenceEnabled("nativeWindowOpen",
  150. web_contents);
  151. process_prefs.disable_popups =
  152. WebContentsPreferences::IsPreferenceEnabled("disablePopups",
  153. web_contents);
  154. AddProcessPreferences(host->GetID(), process_prefs);
  155. // ensure the ProcessPreferences is removed later
  156. host->AddObserver(this);
  157. }
  158. content::SpeechRecognitionManagerDelegate*
  159. AtomBrowserClient::CreateSpeechRecognitionManagerDelegate() {
  160. return new AtomSpeechRecognitionManagerDelegate;
  161. }
  162. void AtomBrowserClient::OverrideWebkitPrefs(
  163. content::RenderViewHost* host, content::WebPreferences* prefs) {
  164. prefs->javascript_enabled = true;
  165. prefs->web_security_enabled = true;
  166. prefs->plugins_enabled = true;
  167. prefs->dom_paste_enabled = true;
  168. prefs->allow_scripts_to_close_windows = true;
  169. prefs->javascript_can_access_clipboard = true;
  170. prefs->local_storage_enabled = true;
  171. prefs->databases_enabled = true;
  172. prefs->application_cache_enabled = true;
  173. prefs->allow_universal_access_from_file_urls = true;
  174. prefs->allow_file_access_from_file_urls = true;
  175. prefs->experimental_webgl_enabled = true;
  176. prefs->allow_running_insecure_content = false;
  177. // Custom preferences of guest page.
  178. auto web_contents = content::WebContents::FromRenderViewHost(host);
  179. WebContentsPreferences::OverrideWebkitPrefs(web_contents, prefs);
  180. }
  181. void AtomBrowserClient::OverrideSiteInstanceForNavigation(
  182. content::RenderFrameHost* rfh,
  183. content::BrowserContext* browser_context,
  184. content::SiteInstance* current_instance,
  185. const GURL& url,
  186. content::SiteInstance** new_instance) {
  187. if (g_suppress_renderer_process_restart) {
  188. g_suppress_renderer_process_restart = false;
  189. return;
  190. }
  191. if (!ShouldCreateNewSiteInstance(rfh, browser_context, current_instance, url))
  192. return;
  193. bool is_new_instance = true;
  194. scoped_refptr<content::SiteInstance> site_instance;
  195. // Do we have an affinity site to manage ?
  196. std::string affinity;
  197. auto* web_contents = content::WebContents::FromRenderFrameHost(rfh);
  198. auto* web_preferences = web_contents ?
  199. WebContentsPreferences::FromWebContents(web_contents) : nullptr;
  200. if (web_preferences &&
  201. web_preferences->web_preferences()->GetString("affinity", &affinity) &&
  202. !affinity.empty()) {
  203. affinity = base::ToLowerASCII(affinity);
  204. auto iter = site_per_affinities.find(affinity);
  205. if (iter != site_per_affinities.end()) {
  206. site_instance = iter->second;
  207. is_new_instance = false;
  208. } else {
  209. // We must not provide the url.
  210. // This site is "isolated" and must not be taken into account
  211. // when Chromium looking at a candidate for an url.
  212. site_instance = content::SiteInstance::Create(
  213. browser_context);
  214. site_per_affinities[affinity] = site_instance.get();
  215. }
  216. } else {
  217. site_instance = content::SiteInstance::CreateForURL(
  218. browser_context,
  219. url);
  220. }
  221. *new_instance = site_instance.get();
  222. if (is_new_instance) {
  223. // Make sure the |site_instance| is not freed
  224. // when this function returns.
  225. // FIXME(zcbenz): We should adjust
  226. // OverrideSiteInstanceForNavigation's interface to solve this.
  227. content::BrowserThread::PostTask(
  228. content::BrowserThread::UI, FROM_HERE,
  229. base::Bind(&Noop, base::RetainedRef(site_instance)));
  230. // Remember the original web contents for the pending renderer process.
  231. auto pending_process = site_instance->GetProcess();
  232. pending_processes_[pending_process->GetID()] = web_contents;
  233. }
  234. }
  235. void AtomBrowserClient::AppendExtraCommandLineSwitches(
  236. base::CommandLine* command_line,
  237. int process_id) {
  238. // Make sure we're about to launch a known executable
  239. {
  240. base::FilePath child_path;
  241. PathService::Get(content::CHILD_PROCESS_EXE, &child_path);
  242. base::ThreadRestrictions::ScopedAllowIO allow_io;
  243. CHECK(base::MakeAbsoluteFilePath(command_line->GetProgram()) == child_path);
  244. }
  245. std::string process_type =
  246. command_line->GetSwitchValueASCII(::switches::kProcessType);
  247. if (process_type != ::switches::kRendererProcess)
  248. return;
  249. // Copy following switches to child process.
  250. static const char* const kCommonSwitchNames[] = {
  251. switches::kStandardSchemes,
  252. switches::kEnableSandbox,
  253. switches::kSecureSchemes
  254. };
  255. command_line->CopySwitchesFrom(
  256. *base::CommandLine::ForCurrentProcess(),
  257. kCommonSwitchNames, arraysize(kCommonSwitchNames));
  258. // The registered service worker schemes.
  259. if (!g_custom_service_worker_schemes.empty())
  260. command_line->AppendSwitchASCII(switches::kRegisterServiceWorkerSchemes,
  261. g_custom_service_worker_schemes);
  262. #if defined(OS_WIN)
  263. // Append --app-user-model-id.
  264. PWSTR current_app_id;
  265. if (SUCCEEDED(GetCurrentProcessExplicitAppUserModelID(&current_app_id))) {
  266. command_line->AppendSwitchNative(switches::kAppUserModelId, current_app_id);
  267. CoTaskMemFree(current_app_id);
  268. }
  269. #endif
  270. if (delegate_) {
  271. auto app_path = static_cast<api::App*>(delegate_)->GetAppPath();
  272. command_line->AppendSwitchPath(switches::kAppPath, app_path);
  273. }
  274. content::WebContents* web_contents = GetWebContentsFromProcessID(process_id);
  275. if (web_contents) {
  276. WebContentsPreferences::AppendExtraCommandLineSwitches(
  277. web_contents, command_line);
  278. SessionPreferences::AppendExtraCommandLineSwitches(
  279. web_contents->GetBrowserContext(), command_line);
  280. auto context_id = atom::api::WebContents::GetIDForContents(
  281. web_contents);
  282. command_line->AppendSwitchASCII(switches::kContextId,
  283. base::IntToString(context_id));
  284. }
  285. }
  286. void AtomBrowserClient::DidCreatePpapiPlugin(
  287. content::BrowserPpapiHost* host) {
  288. host->GetPpapiHost()->AddHostFactoryFilter(
  289. base::WrapUnique(new chrome::ChromeBrowserPepperHostFactory(host)));
  290. }
  291. content::QuotaPermissionContext*
  292. AtomBrowserClient::CreateQuotaPermissionContext() {
  293. return new AtomQuotaPermissionContext;
  294. }
  295. void AtomBrowserClient::AllowCertificateError(
  296. content::WebContents* web_contents,
  297. int cert_error,
  298. const net::SSLInfo& ssl_info,
  299. const GURL& request_url,
  300. content::ResourceType resource_type,
  301. bool overridable,
  302. bool strict_enforcement,
  303. bool expired_previous_decision,
  304. const base::Callback<void(content::CertificateRequestResultType)>&
  305. callback) {
  306. if (delegate_) {
  307. delegate_->AllowCertificateError(
  308. web_contents, cert_error, ssl_info, request_url,
  309. resource_type, overridable, strict_enforcement,
  310. expired_previous_decision, callback);
  311. }
  312. }
  313. void AtomBrowserClient::SelectClientCertificate(
  314. content::WebContents* web_contents,
  315. net::SSLCertRequestInfo* cert_request_info,
  316. net::ClientCertIdentityList client_certs,
  317. std::unique_ptr<content::ClientCertificateDelegate> delegate) {
  318. if (!client_certs.empty() && delegate_) {
  319. delegate_->SelectClientCertificate(web_contents, cert_request_info,
  320. std::move(client_certs),
  321. std::move(delegate));
  322. }
  323. }
  324. void AtomBrowserClient::ResourceDispatcherHostCreated() {
  325. resource_dispatcher_host_delegate_.reset(
  326. new AtomResourceDispatcherHostDelegate);
  327. content::ResourceDispatcherHost::Get()->SetDelegate(
  328. resource_dispatcher_host_delegate_.get());
  329. }
  330. bool AtomBrowserClient::CanCreateWindow(
  331. content::RenderFrameHost* opener,
  332. const GURL& opener_url,
  333. const GURL& opener_top_level_frame_url,
  334. const GURL& source_origin,
  335. content::mojom::WindowContainerType container_type,
  336. const GURL& target_url,
  337. const content::Referrer& referrer,
  338. const std::string& frame_name,
  339. WindowOpenDisposition disposition,
  340. const blink::mojom::WindowFeatures& features,
  341. const std::vector<std::string>& additional_features,
  342. const scoped_refptr<content::ResourceRequestBody>& body,
  343. bool user_gesture,
  344. bool opener_suppressed,
  345. bool* no_javascript_access) {
  346. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  347. int opener_render_process_id = opener->GetProcess()->GetID();
  348. if (IsRendererSandboxed(opener_render_process_id)) {
  349. *no_javascript_access = false;
  350. return true;
  351. }
  352. if (RendererUsesNativeWindowOpen(opener_render_process_id)) {
  353. if (RendererDisablesPopups(opener_render_process_id)) {
  354. // <webview> without allowpopups attribute should return
  355. // null from window.open calls
  356. return false;
  357. } else {
  358. *no_javascript_access = false;
  359. return true;
  360. }
  361. }
  362. if (delegate_) {
  363. return delegate_->CanCreateWindow(
  364. opener, opener_url, opener_top_level_frame_url, source_origin,
  365. container_type, target_url, referrer, frame_name, disposition, features,
  366. additional_features, body, user_gesture, opener_suppressed,
  367. no_javascript_access);
  368. }
  369. return false;
  370. }
  371. void AtomBrowserClient::GetAdditionalAllowedSchemesForFileSystem(
  372. std::vector<std::string>* additional_schemes) {
  373. auto schemes_list = api::GetStandardSchemes();
  374. if (!schemes_list.empty())
  375. additional_schemes->insert(additional_schemes->end(),
  376. schemes_list.begin(),
  377. schemes_list.end());
  378. additional_schemes->push_back(content::kChromeDevToolsScheme);
  379. }
  380. void AtomBrowserClient::SiteInstanceDeleting(
  381. content::SiteInstance* site_instance) {
  382. // We are storing weak_ptr, is it fundamental to maintain the map up-to-date
  383. // when an instance is destroyed.
  384. for (auto iter = site_per_affinities.begin();
  385. iter != site_per_affinities.end(); ++iter) {
  386. if (iter->second == site_instance) {
  387. site_per_affinities.erase(iter);
  388. break;
  389. }
  390. }
  391. }
  392. brightray::BrowserMainParts* AtomBrowserClient::OverrideCreateBrowserMainParts(
  393. const content::MainFunctionParams&) {
  394. return new AtomBrowserMainParts;
  395. }
  396. void AtomBrowserClient::WebNotificationAllowed(
  397. int render_process_id,
  398. const base::Callback<void(bool, bool)>& callback) {
  399. content::WebContents* web_contents =
  400. WebContentsPreferences::GetWebContentsFromProcessID(render_process_id);
  401. if (!web_contents) {
  402. callback.Run(false, false);
  403. return;
  404. }
  405. auto permission_helper =
  406. WebContentsPermissionHelper::FromWebContents(web_contents);
  407. if (!permission_helper) {
  408. callback.Run(false, false);
  409. return;
  410. }
  411. permission_helper->RequestWebNotificationPermission(
  412. base::Bind(callback, web_contents->IsAudioMuted()));
  413. }
  414. void AtomBrowserClient::RenderProcessHostDestroyed(
  415. content::RenderProcessHost* host) {
  416. int process_id = host->GetID();
  417. pending_processes_.erase(process_id);
  418. RemoveProcessPreferences(process_id);
  419. }
  420. void AtomBrowserClient::RenderProcessReady(content::RenderProcessHost* host) {
  421. render_process_host_pids_[host->GetID()] = base::GetProcId(host->GetHandle());
  422. if (delegate_) {
  423. static_cast<api::App*>(delegate_)->RenderProcessReady(host);
  424. }
  425. }
  426. void AtomBrowserClient::RenderProcessExited(content::RenderProcessHost* host,
  427. base::TerminationStatus status,
  428. int exit_code) {
  429. auto host_pid = render_process_host_pids_.find(host->GetID());
  430. if (host_pid != render_process_host_pids_.end()) {
  431. if (delegate_) {
  432. static_cast<api::App*>(delegate_)->RenderProcessDisconnected(
  433. host_pid->second);
  434. }
  435. render_process_host_pids_.erase(host_pid);
  436. }
  437. }
  438. } // namespace atom