diff options
50 files changed, 1208 insertions, 202 deletions
diff --git a/chrome/browser/automation/automation_util.cc b/chrome/browser/automation/automation_util.cc new file mode 100644 index 0000000..10f5da4 --- /dev/null +++ b/chrome/browser/automation/automation_util.cc @@ -0,0 +1,248 @@ +// Copyright (c) 2011 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 "chrome/browser/automation/automation_util.h" + +#include <string> + +#include "base/values.h" +#include "chrome/browser/automation/automation_provider_json.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/renderer_host/browser_render_process_host.h" +#include "chrome/common/net/url_request_context_getter.h" +#include "chrome/browser/ui/browser.h" +#include "content/browser/browser_thread.h" +#include "content/browser/renderer_host/render_view_host.h" +#include "net/base/cookie_store.h" +#include "net/url_request/url_request_context.h" + +namespace { + +void GetCookiesOnIOThread( + const GURL& url, + const scoped_refptr<URLRequestContextGetter>& context_getter, + base::WaitableEvent* event, + std::string* cookies) { + *cookies = context_getter->GetCookieStore()->GetCookies(url); + event->Signal(); +} + +void SetCookieOnIOThread( + const GURL& url, + const std::string& value, + const scoped_refptr<URLRequestContextGetter>& context_getter, + base::WaitableEvent* event, + bool* success) { + *success = context_getter->GetCookieStore()->SetCookie(url, value); + event->Signal(); +} + +void DeleteCookieOnIOThread( + const GURL& url, + const std::string& name, + const scoped_refptr<URLRequestContextGetter>& context_getter, + base::WaitableEvent* event) { + context_getter->GetCookieStore()->DeleteCookie(url, name); + event->Signal(); +} + +} // namespace + +namespace automation_util { + +void GetCookies(const GURL& url, + TabContents* contents, + int* value_size, + std::string* value) { + *value_size = -1; + if (url.is_valid() && contents) { + // Since we may be on the UI thread don't call GetURLRequestContext(). + // Get the request context specific to the current TabContents and app. + const Extension* installed_app = static_cast<BrowserRenderProcessHost*>( + contents->render_view_host()->process())->installed_app(); + scoped_refptr<URLRequestContextGetter> context_getter = + contents->profile()->GetRequestContextForPossibleApp(installed_app); + + base::WaitableEvent event(true /* manual reset */, + false /* not initially signaled */); + CHECK(BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + NewRunnableFunction(&GetCookiesOnIOThread, + url, context_getter, &event, value))); + event.Wait(); + + *value_size = static_cast<int>(value->size()); + } +} + +void SetCookie(const GURL& url, + const std::string value, + TabContents* contents, + int* response_value) { + *response_value = -1; + + if (url.is_valid() && contents) { + // Since we may be on the UI thread don't call GetURLRequestContext(). + // Get the request context specific to the current TabContents and app. + const Extension* installed_app = static_cast<BrowserRenderProcessHost*>( + contents->render_view_host()->process())->installed_app(); + scoped_refptr<URLRequestContextGetter> context_getter = + contents->profile()->GetRequestContextForPossibleApp(installed_app); + + base::WaitableEvent event(true /* manual reset */, + false /* not initially signaled */); + bool success = false; + CHECK(BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + NewRunnableFunction(&SetCookieOnIOThread, + url, value, context_getter, &event, + &success))); + event.Wait(); + if (success) + *response_value = 1; + } +} + +void DeleteCookie(const GURL& url, + const std::string& cookie_name, + TabContents* contents, + bool* success) { + *success = false; + if (url.is_valid() && contents) { + // Since we may be on the UI thread don't call GetURLRequestContext(). + // Get the request context specific to the current TabContents and app. + const Extension* installed_app = static_cast<BrowserRenderProcessHost*>( + contents->render_view_host()->process())->installed_app(); + scoped_refptr<URLRequestContextGetter> context_getter = + contents->profile()->GetRequestContextForPossibleApp(installed_app); + + base::WaitableEvent event(true /* manual reset */, + false /* not initially signaled */); + CHECK(BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + NewRunnableFunction(&DeleteCookieOnIOThread, + url, cookie_name, context_getter, &event))); + event.Wait(); + *success = true; + } +} + +void GetCookiesJSON(AutomationProvider* provider, + DictionaryValue* args, + IPC::Message* reply_message) { + AutomationJSONReply reply(provider, reply_message); + Browser* browser; + std::string error; + if (!GetBrowserFromJSONArgs(args, &browser, &error)) { + reply.SendError(error); + return; + } + std::string url; + if (!args->GetString("url", &url)) { + reply.SendError("'url' missing or invalid"); + return; + } + + // Since we may be on the UI thread don't call GetURLRequestContext(). + scoped_refptr<URLRequestContextGetter> context_getter = + browser->profile()->GetRequestContext(); + + std::string cookies; + base::WaitableEvent event(true /* manual reset */, + false /* not initially signaled */); + Task* task = NewRunnableFunction( + &GetCookiesOnIOThread, + GURL(url), context_getter, &event, &cookies); + if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, task)) { + reply.SendError("Couldn't post task to get the cookies"); + return; + } + event.Wait(); + + DictionaryValue dict; + dict.SetString("cookies", cookies); + reply.SendSuccess(&dict); +} + +void DeleteCookieJSON(AutomationProvider* provider, + DictionaryValue* args, + IPC::Message* reply_message) { + AutomationJSONReply reply(provider, reply_message); + Browser* browser; + std::string error; + if (!GetBrowserFromJSONArgs(args, &browser, &error)) { + reply.SendError(error); + return; + } + std::string url, name; + if (!args->GetString("url", &url)) { + reply.SendError("'url' missing or invalid"); + return; + } + if (!args->GetString("name", &name)) { + reply.SendError("'name' missing or invalid"); + return; + } + + // Since we may be on the UI thread don't call GetURLRequestContext(). + scoped_refptr<URLRequestContextGetter> context_getter = + browser->profile()->GetRequestContext(); + + base::WaitableEvent event(true /* manual reset */, + false /* not initially signaled */); + Task* task = NewRunnableFunction( + &DeleteCookieOnIOThread, + GURL(url), name, context_getter, &event); + if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, task)) { + reply.SendError("Couldn't post task to delete the cookie"); + return; + } + event.Wait(); + reply.SendSuccess(NULL); +} + +void SetCookieJSON(AutomationProvider* provider, + DictionaryValue* args, + IPC::Message* reply_message) { + AutomationJSONReply reply(provider, reply_message); + Browser* browser; + std::string error; + if (!GetBrowserFromJSONArgs(args, &browser, &error)) { + reply.SendError(error); + return; + } + std::string url, cookie; + if (!args->GetString("url", &url)) { + reply.SendError("'url' missing or invalid"); + return; + } + if (!args->GetString("cookie", &cookie)) { + reply.SendError("'cookie' missing or invalid"); + return; + } + + // Since we may be on the UI thread don't call GetURLRequestContext(). + scoped_refptr<URLRequestContextGetter> context_getter = + browser->profile()->GetRequestContext(); + + base::WaitableEvent event(true /* manual reset */, + false /* not initially signaled */); + bool success = false; + Task* task = NewRunnableFunction( + &SetCookieOnIOThread, + GURL(url), cookie, context_getter, &event, &success); + if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, task)) { + reply.SendError("Couldn't post task to set the cookie"); + return; + } + event.Wait(); + + if (!success) { + reply.SendError("Could not set the cookie"); + return; + } + reply.SendSuccess(NULL); +} + +} // namespace automation_util diff --git a/chrome/browser/automation/automation_util.h b/chrome/browser/automation/automation_util.h new file mode 100644 index 0000000..bb38344 --- /dev/null +++ b/chrome/browser/automation/automation_util.h @@ -0,0 +1,72 @@ +// Copyright (c) 2011 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. + +#ifndef CHROME_BROWSER_AUTOMATION_AUTOMATION_UTIL_H_ +#define CHROME_BROWSER_AUTOMATION_AUTOMATION_UTIL_H_ +#pragma once + +#include "base/basictypes.h" +#include "chrome/browser/automation/automation_provider.h" +#include "content/browser/tab_contents/tab_contents.h" + +class DictionaryValue; + +// This file contains automation utility functions. + +namespace automation_util { + +// Gets the size and value of the cookie string for |url| in the given tab. +// Can be called from any thread. +void GetCookies(const GURL& url, + TabContents* contents, + int* value_size, + std::string* value); + +// Sets a cookie for |url| in the given tab. Can be called from any thread. +void SetCookie(const GURL& url, + const std::string value, + TabContents* contents, + int* response_value); + +// Deletes a cookie for |url| in the given tab. Can be called from any thread. +void DeleteCookie(const GURL& url, + const std::string& cookie_name, + TabContents* contents, + bool* success); + +// Gets the cookies for the given URL. Uses the JSON interface. +// Example: +// input: { "windex": 1, "tab_index": 1, "url": "http://www.google.com" } +// output: { "cookies": "PREF=12012" } +void GetCookiesJSON(AutomationProvider* provider, + DictionaryValue* args, + IPC::Message* reply_message); + +// Deletes the cookie with the given name for the URL. Uses the JSON interface. +// Example: +// input: { "windex": 1, +// "tab_index": 1, +// "url": "http://www.google.com", +// "name": "my_cookie" +// } +// output: none +void DeleteCookieJSON(AutomationProvider* provider, + DictionaryValue* args, + IPC::Message* reply_message); + +// Sets a cookie for the given URL. Uses the JSON interface. +// Example: +// input: { "windex": 1, +// "tab_index": 1, +// "url": "http://www.google.com", +// "cookie": "PREF=21321" +// } +// output: none +void SetCookieJSON(AutomationProvider* provider, + DictionaryValue* args, + IPC::Message* reply_message); + +} // namespace automation_util + +#endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_UTIL_H_ diff --git a/chrome/browser/automation/testing_automation_provider.cc b/chrome/browser/automation/testing_automation_provider.cc index a2385b0..476f0ee 100644 --- a/chrome/browser/automation/testing_automation_provider.cc +++ b/chrome/browser/automation/testing_automation_provider.cc @@ -30,6 +30,7 @@ #include "chrome/browser/automation/automation_provider_list.h" #include "chrome/browser/automation/automation_provider_observers.h" #include "chrome/browser/automation/automation_tab_tracker.h" +#include "chrome/browser/automation/automation_util.h" #include "chrome/browser/automation/automation_window_tracker.h" #include "chrome/browser/automation/ui_controls.h" #include "chrome/browser/blocked_content_container.h" @@ -72,7 +73,6 @@ #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" -#include "chrome/common/net/url_request_context_getter.h" #include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" #include "content/browser/renderer_host/render_process_host.h" @@ -81,7 +81,6 @@ #include "content/common/common_param_traits.h" #include "content/common/notification_service.h" #include "net/base/cookie_store.h" -#include "net/url_request/url_request_context.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" #include "ui/base/events.h" #include "ui/base/message_box_flags.h" @@ -89,34 +88,6 @@ namespace { -void GetCookiesOnIOThread( - const GURL& url, - const scoped_refptr<URLRequestContextGetter>& context_getter, - base::WaitableEvent* event, - std::string* cookies) { - *cookies = context_getter->GetCookieStore()->GetCookies(url); - event->Signal(); -} - -void SetCookieOnIOThread( - const GURL& url, - const std::string& value, - const scoped_refptr<URLRequestContextGetter>& context_getter, - base::WaitableEvent* event, - bool* success) { - *success = context_getter->GetCookieStore()->SetCookie(url, value); - event->Signal(); -} - -void DeleteCookieOnIOThread( - const GURL& url, - const std::string& name, - const scoped_refptr<URLRequestContextGetter>& context_getter, - base::WaitableEvent* event) { - context_getter->GetCookieStore()->DeleteCookie(url, name); - event->Signal(); -} - void SendMouseClick(int flags) { ui_controls::MouseButton button = ui_controls::LEFT; if ((flags & ui::EF_LEFT_BUTTON_DOWN) == @@ -508,67 +479,26 @@ void TestingAutomationProvider::CloseTab(int tab_handle, void TestingAutomationProvider::GetCookies(const GURL& url, int handle, int* value_size, std::string* value) { - *value_size = -1; - if (url.is_valid() && tab_tracker_->ContainsHandle(handle)) { - // Since we are running on the UI thread don't call GetURLRequestContext(). - scoped_refptr<URLRequestContextGetter> context_getter = - tab_tracker_->GetResource(handle)->profile()->GetRequestContext(); - - base::WaitableEvent event(true /* manual reset */, - false /* not initially signaled */); - CHECK(BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - NewRunnableFunction(&GetCookiesOnIOThread, - url, context_getter, &event, value))); - event.Wait(); - - *value_size = static_cast<int>(value->size()); - } + TabContents *contents = tab_tracker_->ContainsHandle(handle) ? + tab_tracker_->GetResource(handle)->tab_contents() : NULL; + automation_util::GetCookies(url, contents, value_size, value); } void TestingAutomationProvider::SetCookie(const GURL& url, const std::string value, int handle, int* response_value) { - *response_value = -1; - - if (url.is_valid() && tab_tracker_->ContainsHandle(handle)) { - // Since we are running on the UI thread don't call GetURLRequestContext(). - scoped_refptr<URLRequestContextGetter> context_getter = - tab_tracker_->GetResource(handle)->profile()->GetRequestContext(); - - base::WaitableEvent event(true /* manual reset */, - false /* not initially signaled */); - bool success = false; - CHECK(BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - NewRunnableFunction(&SetCookieOnIOThread, - url, value, context_getter, &event, - &success))); - event.Wait(); - if (success) - *response_value = 1; - } + TabContents *contents = tab_tracker_->ContainsHandle(handle) ? + tab_tracker_->GetResource(handle)->tab_contents() : NULL; + automation_util::SetCookie(url, value, contents, response_value); } void TestingAutomationProvider::DeleteCookie(const GURL& url, const std::string& cookie_name, int handle, bool* success) { - *success = false; - if (url.is_valid() && tab_tracker_->ContainsHandle(handle)) { - // Since we are running on the UI thread don't call GetURLRequestContext(). - scoped_refptr<URLRequestContextGetter> context_getter = - tab_tracker_->GetResource(handle)->profile()->GetRequestContext(); - - base::WaitableEvent event(true /* manual reset */, - false /* not initially signaled */); - CHECK(BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - NewRunnableFunction(&DeleteCookieOnIOThread, - url, cookie_name, context_getter, &event))); - event.Wait(); - *success = true; - } + TabContents *contents = tab_tracker_->ContainsHandle(handle) ? + tab_tracker_->GetResource(handle)->tab_contents() : NULL; + automation_util::DeleteCookie(url, cookie_name, contents, success); } void TestingAutomationProvider::ShowCollectedCookiesDialog( @@ -5024,116 +4954,17 @@ void TestingAutomationProvider::GetTabTitleJSON( void TestingAutomationProvider::GetCookiesJSON( DictionaryValue* args, IPC::Message* reply_message) { - AutomationJSONReply reply(this, reply_message); - Browser* browser; - std::string error; - if (!GetBrowserFromJSONArgs(args, &browser, &error)) { - reply.SendError(error); - return; - } - std::string url; - if (!args->GetString("url", &url)) { - reply.SendError("'url' missing or invalid"); - return; - } - - // Since we are running on the UI thread don't call GetURLRequestContext(). - scoped_refptr<URLRequestContextGetter> context_getter = - browser->profile()->GetRequestContext(); - - std::string cookies; - base::WaitableEvent event(true /* manual reset */, - false /* not initially signaled */); - Task* task = NewRunnableFunction( - &GetCookiesOnIOThread, - GURL(url), context_getter, &event, &cookies); - if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, task)) { - reply.SendError("Couldn't post task to get the cookies"); - return; - } - event.Wait(); - - DictionaryValue dict; - dict.SetString("cookies", cookies); - reply.SendSuccess(&dict); + automation_util::GetCookiesJSON(this, args, reply_message); } void TestingAutomationProvider::DeleteCookieJSON( DictionaryValue* args, IPC::Message* reply_message) { - AutomationJSONReply reply(this, reply_message); - Browser* browser; - std::string error; - if (!GetBrowserFromJSONArgs(args, &browser, &error)) { - reply.SendError(error); - return; - } - std::string url, name; - if (!args->GetString("url", &url)) { - reply.SendError("'url' missing or invalid"); - return; - } - if (!args->GetString("name", &name)) { - reply.SendError("'name' missing or invalid"); - return; - } - - // Since we are running on the UI thread don't call GetURLRequestContext(). - scoped_refptr<URLRequestContextGetter> context_getter = - browser->profile()->GetRequestContext(); - - base::WaitableEvent event(true /* manual reset */, - false /* not initially signaled */); - Task* task = NewRunnableFunction( - &DeleteCookieOnIOThread, - GURL(url), name, context_getter, &event); - if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, task)) { - reply.SendError("Couldn't post task to delete the cookie"); - return; - } - event.Wait(); - reply.SendSuccess(NULL); + automation_util::DeleteCookieJSON(this, args, reply_message); } void TestingAutomationProvider::SetCookieJSON( DictionaryValue* args, IPC::Message* reply_message) { - AutomationJSONReply reply(this, reply_message); - Browser* browser; - std::string error; - if (!GetBrowserFromJSONArgs(args, &browser, &error)) { - reply.SendError(error); - return; - } - std::string url, cookie; - if (!args->GetString("url", &url)) { - reply.SendError("'url' missing or invalid"); - return; - } - if (!args->GetString("cookie", &cookie)) { - reply.SendError("'cookie' missing or invalid"); - return; - } - - // Since we are running on the UI thread don't call GetURLRequestContext(). - scoped_refptr<URLRequestContextGetter> context_getter = - browser->profile()->GetRequestContext(); - - base::WaitableEvent event(true /* manual reset */, - false /* not initially signaled */); - bool success = false; - Task* task = NewRunnableFunction( - &SetCookieOnIOThread, - GURL(url), cookie, context_getter, &event, &success); - if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, task)) { - reply.SendError("Couldn't post task to set the cookie"); - return; - } - event.Wait(); - - if (!success) { - reply.SendError("Could not set the cookie"); - return; - } - reply.SendSuccess(NULL); + automation_util::SetCookieJSON(this, args, reply_message); } void TestingAutomationProvider::GetTabIds( diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index 0c2502c..da6f45a 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc @@ -6,6 +6,7 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/renderer_host/browser_render_process_host.h" #include "content/browser/renderer_host/render_view_host.h" namespace chrome { @@ -19,6 +20,10 @@ void ChromeContentBrowserClient::OnRenderViewCreation( if (service) { bool is_extension_process = service->ExtensionBindingsAllowed(url); render_view_host->set_is_extension_process(is_extension_process); + + const Extension* installed_app = service->GetInstalledApp(url); + static_cast<BrowserRenderProcessHost*>(render_view_host->process())-> + set_installed_app(installed_app); } } diff --git a/chrome/browser/extensions/extension_host.cc b/chrome/browser/extensions/extension_host.cc index 20fd0a4..0566619 100644 --- a/chrome/browser/extensions/extension_host.cc +++ b/chrome/browser/extensions/extension_host.cc @@ -21,6 +21,7 @@ #include "chrome/browser/file_select_helper.h" #include "chrome/browser/platform_util.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/renderer_host/browser_render_process_host.h" #include "chrome/browser/renderer_preferences_util.h" #include "chrome/browser/tab_contents/popup_menu_helper_mac.h" #include "chrome/browser/themes/browser_theme_provider.h" @@ -137,6 +138,11 @@ ExtensionHost::ExtensionHost(const Extension* extension, render_view_host_ = new RenderViewHost(site_instance, this, MSG_ROUTING_NONE, NULL); render_view_host_->set_is_extension_process(true); + if (extension->is_app()) { + BrowserRenderProcessHost* process = static_cast<BrowserRenderProcessHost*>( + render_view_host_->process()); + process->set_installed_app(extension); + } render_view_host_->AllowBindings(BindingsPolicy::EXTENSION); if (enable_dom_automation_) render_view_host_->AllowBindings(BindingsPolicy::DOM_AUTOMATION); diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc index 702fc86..a0d334e 100644 --- a/chrome/browser/extensions/extension_service.cc +++ b/chrome/browser/extensions/extension_service.cc @@ -326,14 +326,22 @@ bool ExtensionService::IsDownloadFromMiniGallery(const GURL& download_url) { false); // case_sensitive } -bool ExtensionService::IsInstalledApp(const GURL& url) { +const Extension* ExtensionService::GetInstalledApp(const GURL& url) { // Check for hosted app. - if (GetExtensionByWebExtent(url) != NULL) - return true; + const Extension* app = GetExtensionByWebExtent(url); + if (app) + return app; // Check for packaged app. - const Extension* extension = GetExtensionByURL(url); - return extension != NULL && extension->is_app(); + app = GetExtensionByURL(url); + if (app && app->is_app()) + return app; + + return NULL; +} + +bool ExtensionService::IsInstalledApp(const GURL& url) { + return !!GetInstalledApp(url); } // static diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h index 32a6356..096319a 100644 --- a/chrome/browser/extensions/extension_service.h +++ b/chrome/browser/extensions/extension_service.h @@ -108,6 +108,9 @@ class ExtensionService // Used to test if we need to show the "Loading" dialog for themes. static bool IsDownloadFromMiniGallery(const GURL& download_url); + // Returns the Extension of hosted or packaged apps, NULL otherwise. + const Extension* GetInstalledApp(const GURL& url); + // Returns whether the URL is from either a hosted or packaged app. bool IsInstalledApp(const GURL& url); diff --git a/chrome/browser/extensions/isolated_app_apitest.cc b/chrome/browser/extensions/isolated_app_apitest.cc new file mode 100644 index 0000000..1d0c5da --- /dev/null +++ b/chrome/browser/extensions/isolated_app_apitest.cc @@ -0,0 +1,141 @@ +// Copyright (c) 2011 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 "base/utf_string_conversions.h" +#include "chrome/browser/automation/automation_util.h" +#include "chrome/browser/extensions/extension_apitest.h" +#include "chrome/browser/extensions/extension_host.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/renderer_host/browser_render_process_host.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/test/ui_test_utils.h" +#include "content/browser/renderer_host/render_view_host.h" +#include "content/browser/tab_contents/tab_contents.h" +#include "net/base/mock_host_resolver.h" + +namespace { + +class IsolatedAppApiTest : public ExtensionApiTest { + public: + // Returns whether the given tab's current URL has the given cookie. + bool WARN_UNUSED_RESULT HasCookie(TabContents* contents, std::string cookie) { + int value_size; + std::string actual_cookie; + automation_util::GetCookies(contents->GetURL(), contents, &value_size, + &actual_cookie); + return actual_cookie.find(cookie) != std::string::npos; + } + + const Extension* GetInstalledApp(TabContents* contents) { + return static_cast<BrowserRenderProcessHost*>( + contents->render_view_host()->process())->installed_app(); + } +}; + +} // namespace + +// Tests that cookies set within an isolated app are not visible to normal +// pages or other apps. +IN_PROC_BROWSER_TEST_F(IsolatedAppApiTest, CookieIsolation) { + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kDisablePopupBlocking); + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableExperimentalAppManifests); + + host_resolver()->AddRule("*", "127.0.0.1"); + ASSERT_TRUE(test_server()->Start()); + + ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("isolated_apps/app1"))); + ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("isolated_apps/app2"))); + + // The app under test acts on URLs whose host is "localhost", + // so the URLs we navigate to must have host "localhost". + GURL base_url = test_server()->GetURL( + "files/extensions/api_test/isolated_apps/"); + GURL::Replacements replace_host; + std::string host_str("localhost"); // Must stay in scope with replace_host. + replace_host.SetHostStr(host_str); + base_url = base_url.ReplaceComponents(replace_host); + + browser()->NewTab(); + ui_test_utils::NavigateToURL(browser(), base_url.Resolve("app1/main.html")); + browser()->NewTab(); + ui_test_utils::NavigateToURL(browser(), base_url.Resolve("app2/main.html")); + browser()->NewTab(); + ui_test_utils::NavigateToURL(browser(), + base_url.Resolve("non_app/main.html")); + + // Ensure first two tabs have installed apps. + TabContents* tab1 = browser()->GetTabContentsAt(1); + TabContents* tab2 = browser()->GetTabContentsAt(2); + TabContents* tab3 = browser()->GetTabContentsAt(3); + ASSERT_TRUE(GetInstalledApp(tab1)); + ASSERT_TRUE(GetInstalledApp(tab2)); + ASSERT_TRUE(!GetInstalledApp(tab3)); + + // Check that each tab sees its own cookie. + ASSERT_TRUE(HasCookie(tab1, "app1=3")); + ASSERT_TRUE(HasCookie(tab2, "app2=4")); + ASSERT_TRUE(HasCookie(tab3, "normalPage=5")); + + // Check that app1 tab cannot see the other cookies. + ASSERT_FALSE(HasCookie(tab1, "app2")); + ASSERT_FALSE(HasCookie(tab1, "normalPage")); + + // Check that app2 tab cannot see the other cookies. + ASSERT_FALSE(HasCookie(tab2, "app1")); + ASSERT_FALSE(HasCookie(tab2, "normalPage")); + + // Check that normal tab cannot see the other cookies. + ASSERT_FALSE(HasCookie(tab3, "app1")); + ASSERT_FALSE(HasCookie(tab3, "app2")); + + // Check that the non_app iframe cookie is associated with app1 and not the + // normal tab. (For now, iframes are always rendered in their parent + // process, even if they aren't in the app manifest.) + ASSERT_TRUE(HasCookie(tab1, "nonAppFrame=6")); + ASSERT_FALSE(HasCookie(tab3, "nonAppFrame")); +} + +// Without the --enable-experimental-app-manifests flag, all the tabs +// should see each others' cookies. +IN_PROC_BROWSER_TEST_F(IsolatedAppApiTest, CookieIsolationRequiresFlag) { + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kDisablePopupBlocking); + + host_resolver()->AddRule("*", "127.0.0.1"); + ASSERT_TRUE(test_server()->Start()); + + ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("isolated_apps/app1"))); + ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("isolated_apps/app2"))); + + // The app under test acts on URLs whose host is "localhost", + // so the URLs we navigate to must have host "localhost". + GURL base_url = test_server()->GetURL( + "files/extensions/api_test/isolated_apps/"); + GURL::Replacements replace_host; + std::string host_str("localhost"); // Must stay in scope with replace_host. + replace_host.SetHostStr(host_str); + base_url = base_url.ReplaceComponents(replace_host); + + browser()->NewTab(); + ui_test_utils::NavigateToURL(browser(), base_url.Resolve("app1/main.html")); + browser()->NewTab(); + ui_test_utils::NavigateToURL(browser(), base_url.Resolve("app2/main.html")); + browser()->NewTab(); + ui_test_utils::NavigateToURL(browser(), + base_url.Resolve("non_app/main.html")); + + // Check that tabs see each others' cookies. + ASSERT_TRUE(HasCookie(browser()->GetTabContentsAt(1), "app2=4")); + ASSERT_TRUE(HasCookie(browser()->GetTabContentsAt(1), "normalPage=5")); + ASSERT_TRUE(HasCookie(browser()->GetTabContentsAt(1), "nonAppFrame=6")); + ASSERT_TRUE(HasCookie(browser()->GetTabContentsAt(2), "app1=3")); + ASSERT_TRUE(HasCookie(browser()->GetTabContentsAt(2), "normalPage=5")); + ASSERT_TRUE(HasCookie(browser()->GetTabContentsAt(2), "nonAppFrame=6")); + ASSERT_TRUE(HasCookie(browser()->GetTabContentsAt(3), "app1=3")); + ASSERT_TRUE(HasCookie(browser()->GetTabContentsAt(3), "app2=4")); + ASSERT_TRUE(HasCookie(browser()->GetTabContentsAt(3), "nonAppFrame=6")); +} diff --git a/chrome/browser/net/chrome_url_request_context.cc b/chrome/browser/net/chrome_url_request_context.cc index 32066a1..e8b68be 100644 --- a/chrome/browser/net/chrome_url_request_context.cc +++ b/chrome/browser/net/chrome_url_request_context.cc @@ -77,6 +77,29 @@ class FactoryForExtensions : public ChromeURLRequestContextFactory { const scoped_refptr<const ProfileIOData> profile_io_data_; }; +// Factory that creates the ChromeURLRequestContext for a given isolated app. +class FactoryForIsolatedApp : public ChromeURLRequestContextFactory { + public: + FactoryForIsolatedApp(const ProfileIOData* profile_io_data, + const std::string& app_id, + ChromeURLRequestContextGetter* main_context) + : profile_io_data_(profile_io_data), + app_id_(app_id), + main_request_context_getter_(main_context) {} + + virtual scoped_refptr<ChromeURLRequestContext> Create() { + // We will copy most of the state from the main request context. + return profile_io_data_->GetIsolatedAppRequestContext( + main_request_context_getter_->GetIOContext(), app_id_); + } + + private: + const scoped_refptr<const ProfileIOData> profile_io_data_; + const std::string app_id_; + scoped_refptr<ChromeURLRequestContextGetter> + main_request_context_getter_; +}; + // Factory that creates the ChromeURLRequestContext for media. class FactoryForMedia : public ChromeURLRequestContextFactory { public: @@ -215,6 +238,20 @@ ChromeURLRequestContextGetter::CreateOriginalForExtensions( // static ChromeURLRequestContextGetter* +ChromeURLRequestContextGetter::CreateOriginalForIsolatedApp( + Profile* profile, + const ProfileIOData* profile_io_data, + const std::string& app_id) { + DCHECK(!profile->IsOffTheRecord()); + ChromeURLRequestContextGetter* main_context = + static_cast<ChromeURLRequestContextGetter*>(profile->GetRequestContext()); + return new ChromeURLRequestContextGetter( + profile, + new FactoryForIsolatedApp(profile_io_data, app_id, main_context)); +} + +// static +ChromeURLRequestContextGetter* ChromeURLRequestContextGetter::CreateOffTheRecord( Profile* profile, const ProfileIOData* profile_io_data) { DCHECK(profile->IsOffTheRecord()); @@ -231,6 +268,20 @@ ChromeURLRequestContextGetter::CreateOffTheRecordForExtensions( profile, new FactoryForExtensions(profile_io_data)); } +// static +ChromeURLRequestContextGetter* +ChromeURLRequestContextGetter::CreateOffTheRecordForIsolatedApp( + Profile* profile, + const ProfileIOData* profile_io_data, + const std::string& app_id) { + DCHECK(profile->IsOffTheRecord()); + ChromeURLRequestContextGetter* main_context = + static_cast<ChromeURLRequestContextGetter*>(profile->GetRequestContext()); + return new ChromeURLRequestContextGetter( + profile, + new FactoryForIsolatedApp(profile_io_data, app_id, main_context)); +} + void ChromeURLRequestContextGetter::CleanupOnUIThread() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // Unregister for pref notifications. @@ -324,6 +375,24 @@ ChromeURLRequestContext::ChromeURLRequestContext() DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); } +void ChromeURLRequestContext::CopyFrom(ChromeURLRequestContext* other) { + URLRequestContext::CopyFrom(other); + + // Copy ChromeURLRequestContext parameters. + set_user_script_dir_path(other->user_script_dir_path()); + set_appcache_service(other->appcache_service()); + set_database_tracker(other->database_tracker()); + set_chrome_cookie_policy(other->chrome_cookie_policy_); + set_host_content_settings_map(other->host_content_settings_map()); + set_host_zoom_map(other->host_zoom_map_); + set_blob_storage_context(other->blob_storage_context()); + set_file_system_context(other->file_system_context()); + set_extension_info_map(other->extension_info_map_); + set_prerender_manager(other->prerender_manager()); + // ChromeURLDataManagerBackend is unique per context. + set_is_incognito(other->is_incognito()); +} + void ChromeURLRequestContext::set_chrome_cookie_policy( ChromeCookiePolicy* cookie_policy) { chrome_cookie_policy_ = cookie_policy; // Take a strong reference. diff --git a/chrome/browser/net/chrome_url_request_context.h b/chrome/browser/net/chrome_url_request_context.h index fab3f71..b8a87d2 100644 --- a/chrome/browser/net/chrome_url_request_context.h +++ b/chrome/browser/net/chrome_url_request_context.h @@ -47,6 +47,9 @@ class ChromeURLRequestContext : public net::URLRequestContext { public: ChromeURLRequestContext(); + // Copies the state from |other| into this context. + void CopyFrom(ChromeURLRequestContext* other); + // Gets the path to the directory user scripts are stored in. FilePath user_script_dir_path() const { return user_script_dir_path_; @@ -140,6 +143,11 @@ class ChromeURLRequestContext : public net::URLRequestContext { virtual ~ChromeURLRequestContext(); private: + // --------------------------------------------------------------------------- + // Important: When adding any new members below, consider whether they need to + // be added to CopyFrom. + // --------------------------------------------------------------------------- + // Path to the directory user scripts are stored in. FilePath user_script_dir_path_; @@ -158,6 +166,11 @@ class ChromeURLRequestContext : public net::URLRequestContext { bool is_incognito_; + // --------------------------------------------------------------------------- + // Important: When adding any new members above, consider whether they need to + // be added to CopyFrom. + // --------------------------------------------------------------------------- + DISALLOW_COPY_AND_ASSIGN(ChromeURLRequestContext); }; @@ -213,6 +226,13 @@ class ChromeURLRequestContextGetter : public URLRequestContextGetter, static ChromeURLRequestContextGetter* CreateOriginalForExtensions( Profile* profile, const ProfileIOData* profile_io_data); + // Create an instance for an original profile for an app with isolated + // storage. This is expected to get called on UI thread. + static ChromeURLRequestContextGetter* CreateOriginalForIsolatedApp( + Profile* profile, + const ProfileIOData* profile_io_data, + const std::string& app_id); + // Create an instance for use with an OTR profile. This is expected to get // called on the UI thread. static ChromeURLRequestContextGetter* CreateOffTheRecord( @@ -223,6 +243,13 @@ class ChromeURLRequestContextGetter : public URLRequestContextGetter, static ChromeURLRequestContextGetter* CreateOffTheRecordForExtensions( Profile* profile, const ProfileIOData* profile_io_data); + // Create an instance for an OTR profile for an app with isolated storage. + // This is expected to get called on UI thread. + static ChromeURLRequestContextGetter* CreateOffTheRecordForIsolatedApp( + Profile* profile, + const ProfileIOData* profile_io_data, + const std::string& app_id); + // Clean up UI thread resources. This is expected to get called on the UI // thread before the instance is deleted on the IO thread. void CleanupOnUIThread(); diff --git a/chrome/browser/net/sqlite_persistent_cookie_store.cc b/chrome/browser/net/sqlite_persistent_cookie_store.cc index 6b92bfd..495efb5 100644 --- a/chrome/browser/net/sqlite_persistent_cookie_store.cc +++ b/chrome/browser/net/sqlite_persistent_cookie_store.cc @@ -18,6 +18,7 @@ #include "base/scoped_ptr.h" #include "base/string_util.h" #include "base/threading/thread.h" +#include "base/threading/thread_restrictions.h" #include "chrome/browser/diagnostics/sqlite_diagnostics.h" #include "content/browser/browser_thread.h" #include "googleurl/src/gurl.h" @@ -156,6 +157,17 @@ bool SQLitePersistentCookieStore::Backend::Load( // This function should be called only once per instance. DCHECK(!db_.get()); + // Ensure the parent directory for storing cookies is created before reading + // from it. We make an exception to allow IO on the UI thread here because + // we are going to disk anyway in db_->Open. (This code will be moved to the + // DB thread as part of http://crbug.com/52909.) + { + base::ThreadRestrictions::ScopedAllowIO allow_io; + const FilePath dir = path_.DirName(); + if (!file_util::PathExists(dir) && !file_util::CreateDirectory(dir)) + return false; + } + db_.reset(new sql::Connection); if (!db_->Open(path_)) { NOTREACHED() << "Unable to open cookie DB."; diff --git a/chrome/browser/notifications/balloon_host.cc b/chrome/browser/notifications/balloon_host.cc index 9dfee2b..a4aa634 100644 --- a/chrome/browser/notifications/balloon_host.cc +++ b/chrome/browser/notifications/balloon_host.cc @@ -6,9 +6,11 @@ #include "chrome/browser/browser_list.h" #include "chrome/browser/extensions/extension_process_manager.h" +#include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/notifications/balloon.h" #include "chrome/browser/notifications/notification.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/renderer_host/browser_render_process_host.h" #include "chrome/browser/renderer_preferences_util.h" #include "chrome/common/bindings_policy.h" #include "chrome/common/render_messages.h" @@ -201,6 +203,11 @@ void BalloonHost::Init() { if (extension_function_dispatcher_.get()) { rvh->AllowBindings(BindingsPolicy::EXTENSION); rvh->set_is_extension_process(true); + const Extension* installed_app = + GetProfile()->GetExtensionService()->GetInstalledApp( + balloon_->notification().content_url()); + static_cast<BrowserRenderProcessHost*>(rvh->process())->set_installed_app( + installed_app); } else if (enable_web_ui_) { rvh->AllowBindings(BindingsPolicy::WEB_UI); } diff --git a/chrome/browser/profiles/off_the_record_profile_io_data.cc b/chrome/browser/profiles/off_the_record_profile_io_data.cc index 2be3631..26cacf1 100644 --- a/chrome/browser/profiles/off_the_record_profile_io_data.cc +++ b/chrome/browser/profiles/off_the_record_profile_io_data.cc @@ -6,6 +6,7 @@ #include "base/command_line.h" #include "base/logging.h" +#include "base/stl_util-inl.h" #include "build/build_config.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/io_thread.h" @@ -14,6 +15,7 @@ #include "chrome/browser/net/chrome_net_log.h" #include "chrome/browser/net/chrome_network_delegate.h" #include "chrome/browser/net/chrome_url_request_context.h" +#include "chrome/common/extensions/extension.h" #include "chrome/common/url_constants.h" #include "content/browser/browser_thread.h" #include "net/ftp/ftp_network_layer.h" @@ -37,6 +39,14 @@ OffTheRecordProfileIOData::Handle::~Handle() { main_request_context_getter_->CleanupOnUIThread(); if (extensions_request_context_getter_) extensions_request_context_getter_->CleanupOnUIThread(); + + // Clean up all isolated app request contexts. + for (ChromeURLRequestContextGetterMap::iterator iter = + app_request_context_getter_map_.begin(); + iter != app_request_context_getter_map_.end(); + ++iter) { + iter->second->CleanupOnUIThread(); + } } scoped_refptr<ChromeURLRequestContextGetter> @@ -66,6 +76,27 @@ OffTheRecordProfileIOData::Handle::GetExtensionsRequestContextGetter() const { return extensions_request_context_getter_; } +scoped_refptr<ChromeURLRequestContextGetter> +OffTheRecordProfileIOData::Handle::GetIsolatedAppRequestContextGetter( + const std::string& app_id) const { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(!app_id.empty()); + LazyInitialize(); + + // Keep a map of request context getters, one per requested app ID. + ChromeURLRequestContextGetterMap::iterator iter = + app_request_context_getter_map_.find(app_id); + if (iter != app_request_context_getter_map_.end()) + return iter->second; + + ChromeURLRequestContextGetter* context = + ChromeURLRequestContextGetter::CreateOffTheRecordForIsolatedApp( + profile_, io_data_, app_id); + app_request_context_getter_map_[app_id] = context; + + return context; +} + void OffTheRecordProfileIOData::Handle::LazyInitialize() const { if (!initialized_) { InitializeProfileParams(profile_, &io_data_->lazy_params_->profile_params); @@ -79,7 +110,9 @@ OffTheRecordProfileIOData::LazyParams::~LazyParams() {} OffTheRecordProfileIOData::OffTheRecordProfileIOData() : ProfileIOData(true), initialized_(false) {} -OffTheRecordProfileIOData::~OffTheRecordProfileIOData() {} +OffTheRecordProfileIOData::~OffTheRecordProfileIOData() { + STLDeleteValues(&app_http_factory_map_); +} void OffTheRecordProfileIOData::LazyInitializeInternal() const { main_request_context_ = new RequestContext; @@ -164,6 +197,37 @@ void OffTheRecordProfileIOData::LazyInitializeInternal() const { new net::FtpNetworkLayer(main_request_context_->host_resolver())); } +scoped_refptr<ProfileIOData::RequestContext> +OffTheRecordProfileIOData::InitializeAppRequestContext( + scoped_refptr<ChromeURLRequestContext> main_context, + const std::string& app_id) const { + scoped_refptr<ProfileIOData::RequestContext> context = new RequestContext; + + // Copy most state from the main context. + context->CopyFrom(main_context); + + // Use a separate in-memory cookie store for the app. + // TODO(creis): We should have a cookie delegate for notifying the cookie + // extensions API, but we need to update it to understand isolated apps first. + context->set_cookie_store( + new net::CookieMonster(NULL, NULL)); + + // Use a separate in-memory cache for the app. + net::HttpCache::BackendFactory* app_backend = + net::HttpCache::DefaultBackend::InMemory(0); + net::HttpNetworkSession* main_network_session = + main_http_factory_->GetSession(); + net::HttpCache* app_http_cache = + new net::HttpCache(main_network_session, app_backend); + + // Keep track of app_http_cache to delete it when we go away. + DCHECK(!app_http_factory_map_[app_id]); + app_http_factory_map_[app_id] = app_http_cache; + context->set_http_transaction_factory(app_http_cache); + + return context; +} + scoped_refptr<ChromeURLRequestContext> OffTheRecordProfileIOData::AcquireMainRequestContext() const { DCHECK(main_request_context_); @@ -187,3 +251,15 @@ OffTheRecordProfileIOData::AcquireExtensionsRequestContext() const { extensions_request_context_ = NULL; return context; } + +scoped_refptr<ChromeURLRequestContext> +OffTheRecordProfileIOData::AcquireIsolatedAppRequestContext( + scoped_refptr<ChromeURLRequestContext> main_context, + const std::string& app_id) const { + // We create per-app contexts on demand, unlike the others above. + scoped_refptr<RequestContext> app_request_context = + InitializeAppRequestContext(main_context, app_id); + DCHECK(app_request_context); + app_request_context->set_profile_io_data(this); + return app_request_context; +} diff --git a/chrome/browser/profiles/off_the_record_profile_io_data.h b/chrome/browser/profiles/off_the_record_profile_io_data.h index 3a6edf5..bac5c43 100644 --- a/chrome/browser/profiles/off_the_record_profile_io_data.h +++ b/chrome/browser/profiles/off_the_record_profile_io_data.h @@ -8,6 +8,7 @@ #include "base/basictypes.h" #include "base/file_path.h" +#include "base/hash_tables.h" #include "base/ref_counted.h" #include "base/scoped_ptr.h" #include "chrome/browser/profiles/profile_io_data.h" @@ -40,8 +41,15 @@ class OffTheRecordProfileIOData : public ProfileIOData { GetMainRequestContextGetter() const; scoped_refptr<ChromeURLRequestContextGetter> GetExtensionsRequestContextGetter() const; + scoped_refptr<ChromeURLRequestContextGetter> + GetIsolatedAppRequestContextGetter( + const std::string& app_id) const; private: + typedef base::hash_map<std::string, + scoped_refptr<ChromeURLRequestContextGetter> > + ChromeURLRequestContextGetterMap; + // Lazily initialize ProfileParams. We do this on the calls to // Get*RequestContextGetter(), so we only initialize ProfileParams right // before posting a task to the IO thread to start using them. This prevents @@ -59,6 +67,8 @@ class OffTheRecordProfileIOData : public ProfileIOData { main_request_context_getter_; mutable scoped_refptr<ChromeURLRequestContextGetter> extensions_request_context_getter_; + mutable ChromeURLRequestContextGetterMap + app_request_context_getter_map_; const scoped_refptr<OffTheRecordProfileIOData> io_data_; Profile* const profile_; @@ -79,17 +89,27 @@ class OffTheRecordProfileIOData : public ProfileIOData { ProfileParams profile_params; }; + typedef base::hash_map<std::string, net::HttpTransactionFactory* > + HttpTransactionFactoryMap; + OffTheRecordProfileIOData(); ~OffTheRecordProfileIOData(); // Lazily initializes ProfileIOData. virtual void LazyInitializeInternal() const; + virtual scoped_refptr<RequestContext> InitializeAppRequestContext( + scoped_refptr<ChromeURLRequestContext> main_context, + const std::string& app_id) const; virtual scoped_refptr<ChromeURLRequestContext> AcquireMainRequestContext() const; virtual scoped_refptr<ChromeURLRequestContext> AcquireMediaRequestContext() const; virtual scoped_refptr<ChromeURLRequestContext> AcquireExtensionsRequestContext() const; + virtual scoped_refptr<ChromeURLRequestContext> + AcquireIsolatedAppRequestContext( + scoped_refptr<ChromeURLRequestContext> main_context, + const std::string& app_id) const; // Lazy initialization params. mutable scoped_ptr<LazyParams> lazy_params_; @@ -105,6 +125,9 @@ class OffTheRecordProfileIOData : public ProfileIOData { mutable scoped_ptr<net::DnsCertProvenanceChecker> dns_cert_checker_; mutable scoped_ptr<net::HttpTransactionFactory> main_http_factory_; + // One HttpTransactionFactory per isolated app. + mutable HttpTransactionFactoryMap app_http_factory_map_; + DISALLOW_COPY_AND_ASSIGN(OffTheRecordProfileIOData); }; diff --git a/chrome/browser/profiles/profile.cc b/chrome/browser/profiles/profile.cc index b56e449..2ec2de4 100644 --- a/chrome/browser/profiles/profile.cc +++ b/chrome/browser/profiles/profile.cc @@ -35,6 +35,7 @@ #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" +#include "chrome/common/extensions/extension.h" #include "chrome/common/json_pref_store.h" #include "chrome/common/pref_names.h" #include "chrome/common/render_messages.h" @@ -419,6 +420,17 @@ class OffTheRecordProfileImpl : public Profile, return io_data_.GetMainRequestContextGetter(); } + virtual URLRequestContextGetter* GetRequestContextForPossibleApp( + const Extension* installed_app) { + if (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableExperimentalAppManifests) && + installed_app != NULL && + installed_app->is_storage_isolated()) + return GetRequestContextForIsolatedApp(installed_app->id()); + + return GetRequestContext(); + } + virtual URLRequestContextGetter* GetRequestContextForMedia() { // In OTR mode, media request context is the same as the original one. return io_data_.GetMainRequestContextGetter(); @@ -428,6 +440,11 @@ class OffTheRecordProfileImpl : public Profile, return io_data_.GetExtensionsRequestContextGetter(); } + URLRequestContextGetter* GetRequestContextForIsolatedApp( + const std::string& app_id) { + return io_data_.GetIsolatedAppRequestContextGetter(app_id); + } + virtual net::SSLConfigService* GetSSLConfigService() { return profile_->GetSSLConfigService(); } diff --git a/chrome/browser/profiles/profile.h b/chrome/browser/profiles/profile.h index 15d3b09..9b70f2c 100644 --- a/chrome/browser/profiles/profile.h +++ b/chrome/browser/profiles/profile.h @@ -346,6 +346,14 @@ class Profile { // happen on the UI thread. virtual URLRequestContextGetter* GetRequestContext() = 0; + // Returns the request context appropriate for the given app. If installed_app + // is null or installed_app->is_storage_isolated() returns false, this is + // equivalent to calling GetRequestContext(). + // TODO(creis): After isolated app storage is no longer an experimental + // feature, consider making this the default contract for GetRequestContext. + virtual URLRequestContextGetter* GetRequestContextForPossibleApp( + const Extension* installed_app) = 0; + // Returns the request context for media resources asociated with this // profile. virtual URLRequestContextGetter* GetRequestContextForMedia() = 0; @@ -354,6 +362,11 @@ class Profile { // is only used for a separate cookie store currently. virtual URLRequestContextGetter* GetRequestContextForExtensions() = 0; + // Returns the request context used within an installed app that has + // requested isolated storage. + virtual URLRequestContextGetter* GetRequestContextForIsolatedApp( + const std::string& app_id) = 0; + // Called by the ExtensionService that lives in this profile. Gives the // profile a chance to react to the load event before the EXTENSION_LOADED // notification has fired. The purpose for handling this event first is to diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc index d973c1f..f500f42 100644 --- a/chrome/browser/profiles/profile_impl.cc +++ b/chrome/browser/profiles/profile_impl.cc @@ -341,10 +341,13 @@ ProfileImpl::ProfileImpl(const FilePath& path) extensions_cookie_path = extensions_cookie_path.Append(chrome::kExtensionsCookieFilename); + FilePath app_path = GetPath().Append(chrome::kIsolatedAppStateDirname); + // Make sure we initialize the ProfileIOData after everything else has been // initialized that we might be reading from the IO thread. io_data_.Init(cookie_path, cache_path, cache_max_size, - media_cache_path, media_cache_max_size, extensions_cookie_path); + media_cache_path, media_cache_max_size, extensions_cookie_path, + app_path); // Initialize the ProfilePolicyConnector after |io_data_| since it requires // the URLRequestContextGetter to be initialized. @@ -821,6 +824,17 @@ URLRequestContextGetter* ProfileImpl::GetRequestContext() { return request_context; } +URLRequestContextGetter* ProfileImpl::GetRequestContextForPossibleApp( + const Extension* installed_app) { + if (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableExperimentalAppManifests) && + installed_app != NULL && + installed_app->is_storage_isolated()) + return GetRequestContextForIsolatedApp(installed_app->id()); + + return GetRequestContext(); +} + URLRequestContextGetter* ProfileImpl::GetRequestContextForMedia() { return io_data_.GetMediaRequestContextGetter(); } @@ -838,6 +852,11 @@ URLRequestContextGetter* ProfileImpl::GetRequestContextForExtensions() { return io_data_.GetExtensionsRequestContextGetter(); } +URLRequestContextGetter* ProfileImpl::GetRequestContextForIsolatedApp( + const std::string& app_id) { + return io_data_.GetIsolatedAppRequestContextGetter(app_id); +} + void ProfileImpl::RegisterExtensionWithRequestContexts( const Extension* extension) { BrowserThread::PostTask( diff --git a/chrome/browser/profiles/profile_impl.h b/chrome/browser/profiles/profile_impl.h index 4544601..d870dea 100644 --- a/chrome/browser/profiles/profile_impl.h +++ b/chrome/browser/profiles/profile_impl.h @@ -86,8 +86,12 @@ class ProfileImpl : public Profile, virtual BrowserThemeProvider* GetThemeProvider(); virtual bool HasCreatedDownloadManager() const; virtual URLRequestContextGetter* GetRequestContext(); + virtual URLRequestContextGetter* GetRequestContextForPossibleApp( + const Extension* installed_app); virtual URLRequestContextGetter* GetRequestContextForMedia(); virtual URLRequestContextGetter* GetRequestContextForExtensions(); + virtual URLRequestContextGetter* GetRequestContextForIsolatedApp( + const std::string& app_id); virtual void RegisterExtensionWithRequestContexts(const Extension* extension); virtual void UnregisterExtensionWithRequestContexts( const Extension* extension); diff --git a/chrome/browser/profiles/profile_impl_io_data.cc b/chrome/browser/profiles/profile_impl_io_data.cc index faffcfb..68b853c 100644 --- a/chrome/browser/profiles/profile_impl_io_data.cc +++ b/chrome/browser/profiles/profile_impl_io_data.cc @@ -5,7 +5,9 @@ #include "chrome/browser/profiles/profile_impl_io_data.h" #include "base/command_line.h" +#include "base/file_util.h" #include "base/logging.h" +#include "base/stl_util-inl.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/io_thread.h" #include "chrome/browser/net/chrome_cookie_policy.h" @@ -15,6 +17,7 @@ #include "chrome/browser/net/sqlite_persistent_cookie_store.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_switches.h" +#include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" #include "content/browser/browser_thread.h" #include "net/ftp/ftp_network_layer.h" @@ -36,6 +39,14 @@ ProfileImplIOData::Handle::~Handle() { media_request_context_getter_->CleanupOnUIThread(); if (extensions_request_context_getter_) extensions_request_context_getter_->CleanupOnUIThread(); + + // Clean up all isolated app request contexts. + for (ChromeURLRequestContextGetterMap::iterator iter = + app_request_context_getter_map_.begin(); + iter != app_request_context_getter_map_.end(); + ++iter) { + iter->second->CleanupOnUIThread(); + } } void ProfileImplIOData::Handle::Init(const FilePath& cookie_path, @@ -43,7 +54,8 @@ void ProfileImplIOData::Handle::Init(const FilePath& cookie_path, int cache_max_size, const FilePath& media_cache_path, int media_cache_max_size, - const FilePath& extensions_cookie_path) { + const FilePath& extensions_cookie_path, + const FilePath& app_path) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DCHECK(!io_data_->lazy_params_.get()); LazyParams* lazy_params = new LazyParams; @@ -58,6 +70,9 @@ void ProfileImplIOData::Handle::Init(const FilePath& cookie_path, lazy_params->io_thread = g_browser_process->io_thread(); io_data_->lazy_params_.reset(lazy_params); + + // Keep track of isolated app path separately so we can use it on demand. + io_data_->app_path_ = app_path; } scoped_refptr<ChromeURLRequestContextGetter> @@ -96,9 +111,33 @@ ProfileImplIOData::Handle::GetExtensionsRequestContextGetter() const { return extensions_request_context_getter_; } +scoped_refptr<ChromeURLRequestContextGetter> +ProfileImplIOData::Handle::GetIsolatedAppRequestContextGetter( + const std::string& app_id) const { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(!app_id.empty()); + LazyInitialize(); + + // Keep a map of request context getters, one per requested app ID. + ChromeURLRequestContextGetterMap::iterator iter = + app_request_context_getter_map_.find(app_id); + if (iter != app_request_context_getter_map_.end()) + return iter->second; + + ChromeURLRequestContextGetter* context = + ChromeURLRequestContextGetter::CreateOriginalForIsolatedApp( + profile_, io_data_, app_id); + app_request_context_getter_map_[app_id] = context; + + return context; +} + void ProfileImplIOData::Handle::LazyInitialize() const { if (!initialized_) { InitializeProfileParams(profile_, &io_data_->lazy_params_->profile_params); + // Keep track of clear_local_state_on_exit for isolated apps. + io_data_->clear_local_state_on_exit_ = + io_data_->lazy_params_->profile_params.clear_local_state_on_exit; initialized_ = true; } } @@ -110,7 +149,9 @@ ProfileImplIOData::LazyParams::LazyParams() ProfileImplIOData::LazyParams::~LazyParams() {} ProfileImplIOData::ProfileImplIOData() : ProfileIOData(false) {} -ProfileImplIOData::~ProfileImplIOData() {} +ProfileImplIOData::~ProfileImplIOData() { + STLDeleteValues(&app_http_factory_map_); +} void ProfileImplIOData::LazyInitializeInternal() const { main_request_context_ = new RequestContext; @@ -255,6 +296,72 @@ void ProfileImplIOData::LazyInitializeInternal() const { lazy_params_.reset(); } +scoped_refptr<ProfileIOData::RequestContext> +ProfileImplIOData::InitializeAppRequestContext( + scoped_refptr<ChromeURLRequestContext> main_context, + const std::string& app_id) const { + scoped_refptr<ProfileIOData::RequestContext> context = new RequestContext; + + // Copy most state from the main context. + context->CopyFrom(main_context); + + FilePath app_path = app_path_.AppendASCII(app_id); + FilePath cookie_path = app_path.Append(chrome::kCookieFilename); + FilePath cache_path = app_path.Append(chrome::kCacheDirname); + // TODO(creis): Determine correct cache size. + int cache_max_size = 0; + + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + bool record_mode = chrome::kRecordModeEnabled && + command_line.HasSwitch(switches::kRecordMode); + bool playback_mode = command_line.HasSwitch(switches::kPlaybackMode); + + // Use a separate HTTP disk cache for isolated apps. + net::HttpCache::DefaultBackend* app_backend = + new net::HttpCache::DefaultBackend( + net::DISK_CACHE, + cache_path, + cache_max_size, + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE)); + net::HttpNetworkSession* main_network_session = + main_http_factory_->GetSession(); + net::HttpCache* app_http_cache = + new net::HttpCache(main_network_session, app_backend); + + scoped_refptr<net::CookieStore> cookie_store = NULL; + if (record_mode || playback_mode) { + // Don't use existing cookies and use an in-memory store. + // TODO(creis): We should have a cookie delegate for notifying the cookie + // extensions API, but we need to update it to understand isolated apps + // first. + cookie_store = new net::CookieMonster(NULL, NULL); + app_http_cache->set_mode( + record_mode ? net::HttpCache::RECORD : net::HttpCache::PLAYBACK); + } + + // Use an app-specific cookie store. + if (!cookie_store) { + DCHECK(!cookie_path.empty()); + + scoped_refptr<SQLitePersistentCookieStore> cookie_db = + new SQLitePersistentCookieStore(cookie_path); + cookie_db->SetClearLocalStateOnExit(clear_local_state_on_exit_); + // TODO(creis): We should have a cookie delegate for notifying the cookie + // extensions API, but we need to update it to understand isolated apps + // first. + cookie_store = new net::CookieMonster(cookie_db.get(), NULL); + } + + context->set_cookie_store(cookie_store); + + // Keep track of app_http_cache to delete it when we go away. + DCHECK(!app_http_factory_map_[app_id]); + app_http_factory_map_[app_id] = app_http_cache; + context->set_http_transaction_factory(app_http_cache); + + return context; +} + scoped_refptr<ChromeURLRequestContext> ProfileImplIOData::AcquireMainRequestContext() const { DCHECK(main_request_context_); @@ -281,3 +388,15 @@ ProfileImplIOData::AcquireExtensionsRequestContext() const { extensions_request_context_ = NULL; return context; } + +scoped_refptr<ChromeURLRequestContext> +ProfileImplIOData::AcquireIsolatedAppRequestContext( + scoped_refptr<ChromeURLRequestContext> main_context, + const std::string& app_id) const { + // We create per-app contexts on demand, unlike the others above. + scoped_refptr<RequestContext> app_request_context = + InitializeAppRequestContext(main_context, app_id); + DCHECK(app_request_context); + app_request_context->set_profile_io_data(this); + return app_request_context; +} diff --git a/chrome/browser/profiles/profile_impl_io_data.h b/chrome/browser/profiles/profile_impl_io_data.h index f7be093..8ab2af1 100644 --- a/chrome/browser/profiles/profile_impl_io_data.h +++ b/chrome/browser/profiles/profile_impl_io_data.h @@ -7,6 +7,7 @@ #pragma once #include "base/basictypes.h" +#include "base/hash_tables.h" #include "base/ref_counted.h" #include "chrome/browser/profiles/profile_io_data.h" @@ -34,7 +35,8 @@ class ProfileImplIOData : public ProfileIOData { int cache_max_size, const FilePath& media_cache_path, int media_cache_max_size, - const FilePath& extensions_cookie_path); + const FilePath& extensions_cookie_path, + const FilePath& app_path); scoped_refptr<ChromeURLRequestContextGetter> GetMainRequestContextGetter() const; @@ -42,8 +44,15 @@ class ProfileImplIOData : public ProfileIOData { GetMediaRequestContextGetter() const; scoped_refptr<ChromeURLRequestContextGetter> GetExtensionsRequestContextGetter() const; + scoped_refptr<ChromeURLRequestContextGetter> + GetIsolatedAppRequestContextGetter( + const std::string& app_id) const; private: + typedef base::hash_map<std::string, + scoped_refptr<ChromeURLRequestContextGetter> > + ChromeURLRequestContextGetterMap; + // Lazily initialize ProfileParams. We do this on the calls to // Get*RequestContextGetter(), so we only initialize ProfileParams right // before posting a task to the IO thread to start using them. This prevents @@ -63,6 +72,7 @@ class ProfileImplIOData : public ProfileIOData { media_request_context_getter_; mutable scoped_refptr<ChromeURLRequestContextGetter> extensions_request_context_getter_; + mutable ChromeURLRequestContextGetterMap app_request_context_getter_map_; const scoped_refptr<ProfileImplIOData> io_data_; Profile* const profile_; @@ -91,17 +101,27 @@ class ProfileImplIOData : public ProfileIOData { ProfileParams profile_params; }; + typedef base::hash_map<std::string, net::HttpTransactionFactory* > + HttpTransactionFactoryMap; + ProfileImplIOData(); virtual ~ProfileImplIOData(); // Lazily initializes ProfileImplIOData. virtual void LazyInitializeInternal() const; + virtual scoped_refptr<RequestContext> InitializeAppRequestContext( + scoped_refptr<ChromeURLRequestContext> main_context, + const std::string& app_id) const; virtual scoped_refptr<ChromeURLRequestContext> AcquireMainRequestContext() const; virtual scoped_refptr<ChromeURLRequestContext> AcquireMediaRequestContext() const; virtual scoped_refptr<ChromeURLRequestContext> AcquireExtensionsRequestContext() const; + virtual scoped_refptr<ChromeURLRequestContext> + AcquireIsolatedAppRequestContext( + scoped_refptr<ChromeURLRequestContext> main_context, + const std::string& app_id) const; // Lazy initialization params. mutable scoped_ptr<LazyParams> lazy_params_; @@ -115,6 +135,13 @@ class ProfileImplIOData : public ProfileIOData { mutable scoped_ptr<net::HttpTransactionFactory> main_http_factory_; mutable scoped_ptr<net::HttpTransactionFactory> media_http_factory_; + // One HttpTransactionFactory per isolated app. + mutable HttpTransactionFactoryMap app_http_factory_map_; + + // Parameters needed for isolated apps. + FilePath app_path_; + bool clear_local_state_on_exit_; + DISALLOW_COPY_AND_ASSIGN(ProfileImplIOData); }; diff --git a/chrome/browser/profiles/profile_io_data.cc b/chrome/browser/profiles/profile_io_data.cc index 3d155fe..3720f2d 100644 --- a/chrome/browser/profiles/profile_io_data.cc +++ b/chrome/browser/profiles/profile_io_data.cc @@ -229,6 +229,17 @@ ProfileIOData::GetExtensionsRequestContext() const { return context; } +scoped_refptr<ChromeURLRequestContext> +ProfileIOData::GetIsolatedAppRequestContext( + scoped_refptr<ChromeURLRequestContext> main_context, + const std::string& app_id) const { + LazyInitialize(); + scoped_refptr<ChromeURLRequestContext> context = + AcquireIsolatedAppRequestContext(main_context, app_id); + DCHECK(context); + return context; +} + void ProfileIOData::LazyInitialize() const { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (initialized_) diff --git a/chrome/browser/profiles/profile_io_data.h b/chrome/browser/profiles/profile_io_data.h index 705ca3a..3a2bcd5 100644 --- a/chrome/browser/profiles/profile_io_data.h +++ b/chrome/browser/profiles/profile_io_data.h @@ -57,12 +57,15 @@ class DatabaseTracker; // accessor. class ProfileIOData : public base::RefCountedThreadSafe<ProfileIOData> { public: - // These should only be called at most once each. Ownership is reversed they - // get called, from ProfileIOData owning ChromeURLRequestContext to vice + // These should only be called at most once each. Ownership is reversed when + // they get called, from ProfileIOData owning ChromeURLRequestContext to vice // versa. scoped_refptr<ChromeURLRequestContext> GetMainRequestContext() const; scoped_refptr<ChromeURLRequestContext> GetMediaRequestContext() const; scoped_refptr<ChromeURLRequestContext> GetExtensionsRequestContext() const; + scoped_refptr<ChromeURLRequestContext> GetIsolatedAppRequestContext( + scoped_refptr<ChromeURLRequestContext> main_context, + const std::string& app_id) const; protected: friend class base::RefCountedThreadSafe<ProfileIOData>; @@ -139,10 +142,16 @@ class ProfileIOData : public base::RefCountedThreadSafe<ProfileIOData> { // Virtual interface for subtypes to implement: // -------------------------------------------- - // Does that actual initialization of the ProfileIOData subtype. Subtypes + // Does the actual initialization of the ProfileIOData subtype. Subtypes // should use the static helper functions above to implement this. virtual void LazyInitializeInternal() const = 0; + // Does an on-demand initialization of a RequestContext for the given + // isolated app. + virtual scoped_refptr<RequestContext> InitializeAppRequestContext( + scoped_refptr<ChromeURLRequestContext> main_context, + const std::string& app_id) const = 0; + // These functions are used to transfer ownership of the lazily initialized // context from ProfileIOData to the URLRequestContextGetter. virtual scoped_refptr<ChromeURLRequestContext> @@ -151,6 +160,10 @@ class ProfileIOData : public base::RefCountedThreadSafe<ProfileIOData> { AcquireMediaRequestContext() const = 0; virtual scoped_refptr<ChromeURLRequestContext> AcquireExtensionsRequestContext() const = 0; + virtual scoped_refptr<ChromeURLRequestContext> + AcquireIsolatedAppRequestContext( + scoped_refptr<ChromeURLRequestContext> main_context, + const std::string& app_id) const = 0; mutable bool initialized_; diff --git a/chrome/browser/renderer_host/browser_render_process_host.cc b/chrome/browser/renderer_host/browser_render_process_host.cc index e4698c9..fc7ed5c 100644 --- a/chrome/browser/renderer_host/browser_render_process_host.cc +++ b/chrome/browser/renderer_host/browser_render_process_host.cc @@ -243,8 +243,10 @@ namespace { class RendererURLRequestContextOverride : public ResourceMessageFilter::URLRequestContextOverride { public: - explicit RendererURLRequestContextOverride(Profile* profile) - : request_context_(profile->GetRequestContext()), + RendererURLRequestContextOverride(Profile* profile, + const Extension* installed_app) + : request_context_(profile->GetRequestContextForPossibleApp( + installed_app)), media_request_context_(profile->GetRequestContextForMedia()) { } @@ -440,11 +442,13 @@ void BrowserRenderProcessHost::CreateMessageFilters() { new RenderMessageFilter(id(), PluginService::GetInstance(), profile(), + profile()->GetRequestContextForPossibleApp( + installed_app_), widget_helper_)); channel_->AddFilter(render_message_filter); scoped_refptr<RendererURLRequestContextOverride> url_request_context_override( - new RendererURLRequestContextOverride(profile())); + new RendererURLRequestContextOverride(profile(), installed_app_)); ResourceMessageFilter* resource_message_filter = new ResourceMessageFilter( id(), ChildProcessInfo::RENDER_PROCESS, diff --git a/chrome/browser/renderer_host/browser_render_process_host.h b/chrome/browser/renderer_host/browser_render_process_host.h index 8d969d1..23a3384 100644 --- a/chrome/browser/renderer_host/browser_render_process_host.h +++ b/chrome/browser/renderer_host/browser_render_process_host.h @@ -16,6 +16,7 @@ #include "base/scoped_callback_factory.h" #include "base/scoped_ptr.h" #include "base/timer.h" +#include "chrome/common/extensions/extension.h" #include "content/browser/child_process_launcher.h" #include "content/browser/renderer_host/render_process_host.h" #include "content/common/notification_observer.h" @@ -52,6 +53,12 @@ class BrowserRenderProcessHost : public RenderProcessHost, explicit BrowserRenderProcessHost(Profile* profile); ~BrowserRenderProcessHost(); + // Whether this process is associated with an installed app. + const Extension* installed_app() const { return installed_app_; } + void set_installed_app(const Extension* installed_app) { + installed_app_ = installed_app; + } + // RenderProcessHost implementation (public portion). virtual bool Init(bool is_accessibility_enabled, bool is_extensions_process); virtual int GetNextRoutingID(); @@ -199,7 +206,10 @@ class BrowserRenderProcessHost : public RenderProcessHost, // when running in single-process mode. bool extension_process_; - // Usedt to launch and terminate the process without blocking the UI thread. + // The Extension for the hosted or packaged app if any, NULL otherwise. + scoped_refptr<const Extension> installed_app_; + + // Used to launch and terminate the process without blocking the UI thread. scoped_ptr<ChildProcessLauncher> child_process_; // Messages we queue while waiting for the process handle. We queue them here diff --git a/chrome/browser/sidebar/sidebar_container.cc b/chrome/browser/sidebar/sidebar_container.cc index cab6dc2..529eb8f 100644 --- a/chrome/browser/sidebar/sidebar_container.cc +++ b/chrome/browser/sidebar/sidebar_container.cc @@ -6,6 +6,7 @@ #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/renderer_host/browser_render_process_host.h" #include "chrome/common/bindings_policy.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_resource.h" @@ -32,6 +33,12 @@ SidebarContainer::SidebarContainer(TabContents* tab, sidebar_contents_.reset( new TabContents(tab->profile(), NULL, MSG_ROUTING_NONE, NULL, NULL)); sidebar_contents_->render_view_host()->set_is_extension_process(true); + const Extension* extension = GetExtension(); + if (extension && extension->is_app()) { + BrowserRenderProcessHost* process = static_cast<BrowserRenderProcessHost*>( + sidebar_contents_->render_view_host()->process()); + process->set_installed_app(extension); + } sidebar_contents_->render_view_host()->AllowBindings( BindingsPolicy::EXTENSION); sidebar_contents_->set_delegate(this); diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 16898a7..d958d47 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -203,6 +203,8 @@ 'browser/automation/automation_autocomplete_edit_tracker.h', 'browser/automation/automation_browser_tracker.cc', 'browser/automation/automation_browser_tracker.h', + 'browser/automation/automation_util.cc', + 'browser/automation/automation_util.h', 'browser/automation/automation_extension_function.cc', 'browser/automation/automation_extension_function.h', 'browser/automation/automation_extension_tracker.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index b6b26cab..c41abc7 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -2242,6 +2242,7 @@ 'browser/extensions/extension_webrequest_apitest.cc', 'browser/extensions/extension_websocket_apitest.cc', 'browser/extensions/extension_webstore_private_browsertest.cc', + 'browser/extensions/isolated_app_apitest.cc', 'browser/extensions/notifications_apitest.cc', 'browser/extensions/page_action_apitest.cc', 'browser/extensions/permissions_apitest.cc', diff --git a/chrome/common/chrome_constants.cc b/chrome/common/chrome_constants.cc index f1752c8..0ccf500 100644 --- a/chrome/common/chrome_constants.cc +++ b/chrome/common/chrome_constants.cc @@ -84,6 +84,7 @@ const FilePath::CharType kAppCacheDirname[] = FPL("Application Cache"); const FilePath::CharType kThemePackFilename[] = FPL("Cached Theme.pak"); const FilePath::CharType kCookieFilename[] = FPL("Cookies"); const FilePath::CharType kExtensionsCookieFilename[] = FPL("Extension Cookies"); +const FilePath::CharType kIsolatedAppStateDirname[] = FPL("Isolated Apps"); const FilePath::CharType kFaviconsFilename[] = FPL("Favicons"); const FilePath::CharType kHistoryFilename[] = FPL("History"); const FilePath::CharType kLocalStateFilename[] = FPL("Local State"); diff --git a/chrome/common/chrome_constants.h b/chrome/common/chrome_constants.h index 5a4befc..0070a0f 100644 --- a/chrome/common/chrome_constants.h +++ b/chrome/common/chrome_constants.h @@ -46,6 +46,7 @@ extern const FilePath::CharType kAppCacheDirname[]; extern const FilePath::CharType kThemePackFilename[]; extern const FilePath::CharType kCookieFilename[]; extern const FilePath::CharType kExtensionsCookieFilename[]; +extern const FilePath::CharType kIsolatedAppStateDirname[]; extern const FilePath::CharType kFaviconsFilename[]; extern const FilePath::CharType kHistoryFilename[]; extern const FilePath::CharType kLocalStateFilename[]; diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index ea8c178..a323cc3 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -457,6 +457,10 @@ const char kEnableDNSCertProvenanceChecking[] = const char kEnableDNSSECCerts[] = "enable-dnssec-certs"; +// Enables app manifest features that are in development. +const char kEnableExperimentalAppManifests[] = + "enable-experimental-app-manifests"; + // Enables extension APIs that are in development. const char kEnableExperimentalExtensionApis[] = "enable-experimental-extension-apis"; diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index a8d9fea..e297a72 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -139,6 +139,7 @@ extern const char kEnableDataTransferItems[]; extern const char kEnableDeviceMotion[]; extern const char kEnableDNSCertProvenanceChecking[]; extern const char kEnableDNSSECCerts[]; +extern const char kEnableExperimentalAppManifests[]; extern const char kEnableExperimentalExtensionApis[]; extern const char kEnableExtensionTimelineApi[]; extern const char kEnableFastback[]; diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc index 0be0dbc..c86ba40 100644 --- a/chrome/common/extensions/extension.cc +++ b/chrome/common/extensions/extension.cc @@ -1140,6 +1140,43 @@ bool Extension::LoadLaunchContainer(const DictionaryValue* manifest, return true; } +bool Extension::LoadAppIsolation(const DictionaryValue* manifest, + std::string* error) { + // Only parse app isolation features if this switch is present. + if (!CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableExperimentalAppManifests)) + return true; + + Value* temp = NULL; + if (!manifest->Get(keys::kIsolation, &temp)) + return true; + + if (temp->GetType() != Value::TYPE_LIST) { + *error = errors::kInvalidIsolation; + return false; + } + + ListValue* isolation_list = static_cast<ListValue*>(temp); + for (size_t i = 0; i < isolation_list->GetSize(); ++i) { + std::string isolation_string; + if (!isolation_list->GetString(i, &isolation_string)) { + *error = ExtensionErrorUtils::FormatErrorMessage( + errors::kInvalidIsolationValue, + base::UintToString(i)); + return false; + } + + // Check for isolated storage. + if (isolation_string == values::kIsolatedStorage) { + is_storage_isolated_ = true; + } else { + LOG(WARNING) << "Did not recognize isolation type: " + << isolation_string; + } + } + return true; +} + bool Extension::EnsureNotHybridApp(const DictionaryValue* manifest, std::string* error) { if (web_extent().is_empty()) @@ -1165,6 +1202,7 @@ Extension::Extension(const FilePath& path, Location location) converted_from_user_script_(false), is_theme_(false), is_app_(false), + is_storage_isolated_(false), launch_container_(extension_misc::LAUNCH_TAB), launch_width_(0), launch_height_(0) { @@ -1803,7 +1841,8 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, parse_strictness, error) || !EnsureNotHybridApp(manifest_value_.get(), error) || !LoadLaunchURL(manifest_value_.get(), error) || - !LoadLaunchContainer(manifest_value_.get(), error)) { + !LoadLaunchContainer(manifest_value_.get(), error) || + !LoadAppIsolation(manifest_value_.get(), error)) { return false; } diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h index 7e2b593..5f934f3 100644 --- a/chrome/common/extensions/extension.h +++ b/chrome/common/extensions/extension.h @@ -483,6 +483,7 @@ class Extension : public base::RefCountedThreadSafe<Extension> { bool is_app() const { return is_app_; } bool is_hosted_app() const { return is_app() && !web_extent().is_empty(); } bool is_packaged_app() const { return is_app() && web_extent().is_empty(); } + bool is_storage_isolated() const { return is_app() && is_storage_isolated_; } const ExtensionExtent& web_extent() const { return extent_; } const std::string& launch_local_path() const { return launch_local_path_; } const std::string& launch_web_url() const { return launch_web_url_; } @@ -574,6 +575,7 @@ class Extension : public base::RefCountedThreadSafe<Extension> { std::string* error); bool LoadLaunchContainer(const DictionaryValue* manifest, std::string* error); bool LoadLaunchURL(const DictionaryValue* manifest, std::string* error); + bool LoadAppIsolation(const DictionaryValue* manifest, std::string* error); bool EnsureNotHybridApp(const DictionaryValue* manifest, std::string* error); // Helper method to load an ExtensionAction from the page_action or @@ -736,6 +738,9 @@ class Extension : public base::RefCountedThreadSafe<Extension> { // Whether this extension uses app features. bool is_app_; + // Whether this extension requests isolated storage. + bool is_storage_isolated_; + // The local path inside the extension to use with the launcher. std::string launch_local_path_; diff --git a/chrome/common/extensions/extension_constants.cc b/chrome/common/extensions/extension_constants.cc index 96fd349..0facbf9 100644 --- a/chrome/common/extensions/extension_constants.cc +++ b/chrome/common/extensions/extension_constants.cc @@ -23,6 +23,7 @@ const char* kHomepageURL = "homepage_url"; const char* kIcons = "icons"; const char* kIncognito = "incognito"; const char* kIncludeGlobs = "include_globs"; +const char* kIsolation = "app.isolation"; const char* kJs = "js"; const char* kLaunch = "app.launch"; const char* kLaunchContainer = "app.launch.container"; @@ -80,6 +81,7 @@ const char* kWebURLs = "app.urls"; namespace extension_manifest_values { const char* kIncognitoSplit = "split"; const char* kIncognitoSpanning = "spanning"; +const char* kIsolatedStorage = "storage"; const char* kRunAtDocumentStart = "document_start"; const char* kRunAtDocumentEnd = "document_end"; const char* kRunAtDocumentIdle = "document_idle"; @@ -153,6 +155,10 @@ const char* kInvalidIcons = "Invalid value for 'icons'."; const char* kInvalidIncognitoBehavior = "Invalid value for 'incognito'."; +const char* kInvalidIsolation = + "Invalid value for 'app.isolation'."; +const char* kInvalidIsolationValue = + "Invalid value for 'app.isolation[*]'."; const char* kInvalidJs = "Invalid value for 'content_scripts[*].js[*]'."; const char* kInvalidJsList = diff --git a/chrome/common/extensions/extension_constants.h b/chrome/common/extensions/extension_constants.h index bad2a5b..3e8e130 100644 --- a/chrome/common/extensions/extension_constants.h +++ b/chrome/common/extensions/extension_constants.h @@ -28,6 +28,7 @@ namespace extension_manifest_keys { extern const char* kIcons; extern const char* kIncognito; extern const char* kIncludeGlobs; + extern const char* kIsolation; extern const char* kJs; extern const char* kLaunch; extern const char* kLaunchContainer; @@ -86,6 +87,7 @@ namespace extension_manifest_keys { namespace extension_manifest_values { extern const char* kIncognitoSplit; extern const char* kIncognitoSpanning; + extern const char* kIsolatedStorage; extern const char* kLaunchContainerPanel; extern const char* kLaunchContainerTab; extern const char* kLaunchContainerWindow; @@ -128,6 +130,8 @@ namespace extension_manifest_errors { extern const char* kInvalidIconPath; extern const char* kInvalidIcons; extern const char* kInvalidIncognitoBehavior; + extern const char* kInvalidIsolation; + extern const char* kInvalidIsolationValue; extern const char* kInvalidJs; extern const char* kInvalidJsList; extern const char* kInvalidKey; diff --git a/chrome/common/extensions/extension_manifests_unittest.cc b/chrome/common/extensions/extension_manifests_unittest.cc index 63172a9..91951ea 100644 --- a/chrome/common/extensions/extension_manifests_unittest.cc +++ b/chrome/common/extensions/extension_manifests_unittest.cc @@ -547,3 +547,18 @@ TEST_F(ExtensionManifestTest, ForbidPortsInPermissions) { // to flag this case. LoadStrictAndExpectSuccess("forbid_ports_in_permissions.json"); } + +TEST_F(ExtensionManifestTest, IsolatedApps) { + // Requires --enable-experimental-app-manifests + scoped_refptr<Extension> extension( + LoadAndExpectSuccess("isolated_app_valid.json")); + EXPECT_FALSE(extension->is_storage_isolated()); + + CommandLine old_command_line = *CommandLine::ForCurrentProcess(); + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableExperimentalAppManifests); + scoped_refptr<Extension> extension2( + LoadAndExpectSuccess("isolated_app_valid.json")); + EXPECT_TRUE(extension2->is_storage_isolated()); + *CommandLine::ForCurrentProcess() = old_command_line; +} diff --git a/chrome/test/data/extensions/api_test/app_process/manifest.json b/chrome/test/data/extensions/api_test/app_process/manifest.json index 2e35457..42a95fc 100644 --- a/chrome/test/data/extensions/api_test/app_process/manifest.json +++ b/chrome/test/data/extensions/api_test/app_process/manifest.json @@ -11,7 +11,7 @@ "http://localhost/files/extensions/api_test/app_process/path4" ], "launch": { - "web_url": "http://localhost:1337/files/extensions/api_test/app_process/path1/foo.html" + "web_url": "http://localhost/files/extensions/api_test/app_process/path1/foo.html" } } } diff --git a/chrome/test/data/extensions/api_test/isolated_apps/app1/main.html b/chrome/test/data/extensions/api_test/isolated_apps/app1/main.html new file mode 100644 index 0000000..427902c --- /dev/null +++ b/chrome/test/data/extensions/api_test/isolated_apps/app1/main.html @@ -0,0 +1,15 @@ +<html> +<body> +Isolated App 1 + +<script> + // Set a cookie within the app. + var expire = new Date(); + expire.setDate(expire.getDate() + 1); // tomorrow + document.cookie = "app1=3; path=/; expires=" + expire + ";" +</script> + +<!-- Include an iframe to a non-app URL. --> +<iframe src="../non_app/subframe.html"></iframe> +</body> +</html> diff --git a/chrome/test/data/extensions/api_test/isolated_apps/app1/manifest.json b/chrome/test/data/extensions/api_test/isolated_apps/app1/manifest.json new file mode 100644 index 0000000..2ef8d1c --- /dev/null +++ b/chrome/test/data/extensions/api_test/isolated_apps/app1/manifest.json @@ -0,0 +1,16 @@ +{ + "name": "isolated_app1", + "version": "0.1", + "description": "Tests isolation of stored data for apps.", + "app": { + "urls": [ + "http://localhost/files/extensions/api_test/isolated_apps/app1" + ], + "launch": { + "web_url": "http://localhost/files/extensions/api_test/isolated_apps/app1/main.html" + }, + "isolation": [ + "storage" + ] + } +} diff --git a/chrome/test/data/extensions/api_test/isolated_apps/app2/main.html b/chrome/test/data/extensions/api_test/isolated_apps/app2/main.html new file mode 100644 index 0000000..506e874 --- /dev/null +++ b/chrome/test/data/extensions/api_test/isolated_apps/app2/main.html @@ -0,0 +1,12 @@ +<html> +<body> +Isolated App 2 + +<script> + // Set a cookie within the app. + var expire = new Date(); + expire.setDate(expire.getDate() + 1); // tomorrow + document.cookie = "app2=4; path=/; expires=" + expire + ";" +</script> +</body> +</html> diff --git a/chrome/test/data/extensions/api_test/isolated_apps/app2/manifest.json b/chrome/test/data/extensions/api_test/isolated_apps/app2/manifest.json new file mode 100644 index 0000000..5ce184c --- /dev/null +++ b/chrome/test/data/extensions/api_test/isolated_apps/app2/manifest.json @@ -0,0 +1,16 @@ +{ + "name": "isolated_app2", + "version": "0.1", + "description": "Tests isolation of stored data for apps.", + "app": { + "urls": [ + "http://localhost/files/extensions/api_test/isolated_apps/app2" + ], + "launch": { + "web_url": "http://localhost/files/extensions/api_test/isolated_apps/app2/main.html" + }, + "isolation": [ + "storage" + ] + } +} diff --git a/chrome/test/data/extensions/api_test/isolated_apps/non_app/main.html b/chrome/test/data/extensions/api_test/isolated_apps/non_app/main.html new file mode 100644 index 0000000..ab8c9b31 --- /dev/null +++ b/chrome/test/data/extensions/api_test/isolated_apps/non_app/main.html @@ -0,0 +1,12 @@ +<html> +<body> +Normal page + +<script> + // Set a cookie outside any apps. + var expire = new Date(); + expire.setDate(expire.getDate() + 1); // tomorrow + document.cookie = "normalPage=5; path=/; expires=" + expire + ";" +</script> +</body> +</html> diff --git a/chrome/test/data/extensions/api_test/isolated_apps/non_app/subframe.html b/chrome/test/data/extensions/api_test/isolated_apps/non_app/subframe.html new file mode 100644 index 0000000..41fb7e7 --- /dev/null +++ b/chrome/test/data/extensions/api_test/isolated_apps/non_app/subframe.html @@ -0,0 +1,12 @@ +<html> +<body> +Normal page + +<script> + // Set a cookie from a page in an iframe. + var expire = new Date(); + expire.setDate(expire.getDate() + 1); // tomorrow + document.cookie = "nonAppFrame=6; path=/; expires=" + expire + ";" +</script> +</body> +</html> diff --git a/chrome/test/data/extensions/manifest_tests/isolated_app_valid.json b/chrome/test/data/extensions/manifest_tests/isolated_app_valid.json new file mode 100644 index 0000000..eb2ccb0 --- /dev/null +++ b/chrome/test/data/extensions/manifest_tests/isolated_app_valid.json @@ -0,0 +1,15 @@ +{ + "name": "test", + "version": "1", + "app": { + "urls": [ + "http://www.google.com/mail/" + ], + "launch": { + "web_url": "http://www.google.com/mail/" + }, + "isolation": [ + "storage" + ] + } +} diff --git a/chrome/test/testing_profile.cc b/chrome/test/testing_profile.cc index 2534ef0..a348337 100644 --- a/chrome/test/testing_profile.cc +++ b/chrome/test/testing_profile.cc @@ -563,6 +563,14 @@ URLRequestContextGetter* TestingProfile::GetRequestContext() { return request_context_.get(); } +URLRequestContextGetter* TestingProfile::GetRequestContextForPossibleApp( + const Extension* installed_app) { + if (installed_app != NULL && installed_app->is_storage_isolated()) + return GetRequestContextForIsolatedApp(installed_app->id()); + + return GetRequestContext(); +} + void TestingProfile::CreateRequestContext() { if (!request_context_) request_context_ = new TestURLRequestContextGetter(); @@ -590,6 +598,13 @@ UserStyleSheetWatcher* TestingProfile::GetUserStyleSheetWatcher() { return NULL; } +URLRequestContextGetter* TestingProfile::GetRequestContextForIsolatedApp( + const std::string& app_id) { + // We don't test isolated app storage here yet, so returning the same dummy + // context is sufficient for now. + return GetRequestContext(); +} + FindBarState* TestingProfile::GetFindBarState() { if (!find_bar_state_.get()) find_bar_state_.reset(new FindBarState()); diff --git a/chrome/test/testing_profile.h b/chrome/test/testing_profile.h index 49063b5..3ae70ba 100644 --- a/chrome/test/testing_profile.h +++ b/chrome/test/testing_profile.h @@ -201,6 +201,8 @@ class TestingProfile : public Profile { // getter is currently only capable of returning a Context that helps test // the CookieMonster. See implementation comments for more details. virtual URLRequestContextGetter* GetRequestContext(); + virtual URLRequestContextGetter* GetRequestContextForPossibleApp( + const Extension* installed_app); void CreateRequestContext(); // Clears out the created request context (which must be done before shutting // down the IO thread to avoid leaks). @@ -208,6 +210,8 @@ class TestingProfile : public Profile { virtual URLRequestContextGetter* GetRequestContextForMedia(); virtual URLRequestContextGetter* GetRequestContextForExtensions(); + virtual URLRequestContextGetter* GetRequestContextForIsolatedApp( + const std::string& app_id); virtual net::SSLConfigService* GetSSLConfigService(); virtual UserStyleSheetWatcher* GetUserStyleSheetWatcher(); diff --git a/content/browser/renderer_host/render_message_filter.cc b/content/browser/renderer_host/render_message_filter.cc index 0c5c83a..7683d4f 100644 --- a/content/browser/renderer_host/render_message_filter.cc +++ b/content/browser/renderer_host/render_message_filter.cc @@ -279,11 +279,13 @@ RenderMessageFilter::RenderMessageFilter( int render_process_id, PluginService* plugin_service, Profile* profile, + URLRequestContextGetter* request_context, RenderWidgetHelper* render_widget_helper) : resource_dispatcher_host_(g_browser_process->resource_dispatcher_host()), plugin_service_(plugin_service), profile_(profile), content_settings_(profile->GetHostContentSettingsMap()), + request_context_(request_context), extensions_request_context_(profile->GetRequestContextForExtensions()), render_widget_helper_(render_widget_helper), notification_prefs_( @@ -292,7 +294,6 @@ RenderMessageFilter::RenderMessageFilter( off_the_record_(profile->IsOffTheRecord()), webkit_context_(profile->GetWebKitContext()), render_process_id_(render_process_id) { - request_context_ = profile_->GetRequestContext(); DCHECK(request_context_); render_widget_helper_->Init(render_process_id_, resource_dispatcher_host_); diff --git a/content/browser/renderer_host/render_message_filter.h b/content/browser/renderer_host/render_message_filter.h index ab6ba38..18b35df 100644 --- a/content/browser/renderer_host/render_message_filter.h +++ b/content/browser/renderer_host/render_message_filter.h @@ -61,6 +61,7 @@ class RenderMessageFilter : public BrowserMessageFilter { RenderMessageFilter(int render_process_id, PluginService* plugin_service, Profile* profile, + URLRequestContextGetter* request_context, RenderWidgetHelper* render_widget_helper); // BrowserMessageFilter methods: diff --git a/net/url_request/url_request_context.cc b/net/url_request/url_request_context.cc index 2f558cc..7e02641 100644 --- a/net/url_request/url_request_context.cc +++ b/net/url_request/url_request_context.cc @@ -27,6 +27,29 @@ URLRequestContext::URLRequestContext() ftp_transaction_factory_(NULL) { } +void URLRequestContext::CopyFrom(URLRequestContext* other) { + // Copy URLRequestContext parameters. + // Do not copy is_main_. + set_net_log(other->net_log()); + set_host_resolver(other->host_resolver()); + set_cert_verifier(other->cert_verifier()); + set_dnsrr_resolver(other->dnsrr_resolver()); + set_dns_cert_checker(other->dns_cert_checker()); + set_http_auth_handler_factory(other->http_auth_handler_factory()); + set_proxy_service(other->proxy_service()); + set_ssl_config_service(other->ssl_config_service()); + set_network_delegate(other->network_delegate()); + set_cookie_store(other->cookie_store()); + set_cookie_policy(other->cookie_policy()); + set_transport_security_state(other->transport_security_state()); + // FTPAuthCache is unique per context. + set_accept_language(other->accept_language()); + set_accept_charset(other->accept_charset()); + set_referrer_charset(other->referrer_charset()); + set_http_transaction_factory(other->http_transaction_factory()); + set_ftp_transaction_factory(other->ftp_transaction_factory()); +} + void URLRequestContext::set_cookie_store(CookieStore* cookie_store) { cookie_store_ = cookie_store; } diff --git a/net/url_request/url_request_context.h b/net/url_request/url_request_context.h index 7976126..63e069a 100644 --- a/net/url_request/url_request_context.h +++ b/net/url_request/url_request_context.h @@ -44,6 +44,9 @@ class URLRequestContext public: URLRequestContext(); + // Copies the state from |other| into this context. + void CopyFrom(URLRequestContext* other); + NetLog* net_log() const { return net_log_; } @@ -183,6 +186,11 @@ class URLRequestContext virtual ~URLRequestContext(); private: + // --------------------------------------------------------------------------- + // Important: When adding any new members below, consider whether they need to + // be added to CopyFrom. + // --------------------------------------------------------------------------- + // Indicates whether or not this is the main URLRequestContext. bool is_main_; @@ -211,6 +219,11 @@ class URLRequestContext HttpTransactionFactory* http_transaction_factory_; FtpTransactionFactory* ftp_transaction_factory_; + // --------------------------------------------------------------------------- + // Important: When adding any new members below, consider whether they need to + // be added to CopyFrom. + // --------------------------------------------------------------------------- + DISALLOW_COPY_AND_ASSIGN(URLRequestContext); }; |