diff options
author | creis@google.com <creis@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-15 22:45:09 +0000 |
---|---|---|
committer | creis@google.com <creis@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-15 22:45:09 +0000 |
commit | d969667a1e558d2ba53e556233598cdc75d24678 (patch) | |
tree | 5b848ba7627e1240c0ef2855bf5780db83a027b7 /chrome | |
parent | 71e2f0a1ba62594c2cb555dd291810aaa7775779 (diff) | |
download | chromium_src-d969667a1e558d2ba53e556233598cdc75d24678.zip chromium_src-d969667a1e558d2ba53e556233598cdc75d24678.tar.gz chromium_src-d969667a1e558d2ba53e556233598cdc75d24678.tar.bz2 |
Initial support for partitioning cookies for isolated apps.
This CL adds experimental support for letting installed apps request isolated
storage in their manifest. An isolated app will have its own cookie store
that is not shared with other apps or normal pages, even if they share an
origin. The feature is currently behind a --enable-experimental-app-manifests
flag.
BUG=69335
TEST=ExtensionManifestTest.IsolatedApps
TEST=IsolatedAppApiTest.CookieIsolation*
Review URL: http://codereview.chromium.org/6201005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@78301 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
46 files changed, 1169 insertions, 201 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(); |